抓取网站文章

本段代码主要功能更具文章列表分页抓取文章,主要使用了jsoup。里面所有的选择器都可jQuery里面的选择器类似。

代码如下:

package com.acgist.spider;
 
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
 
import org.apache.commons.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
 
/**
 * 文章抓取蜘蛛
 */
public class Spider {
 
    private String domain; // 文章链接地址
    private String domainName; // 文章网站名称
    private String firstPage; // 第一页
    private String pageSelector; // 文章分页列表选择器
    private String listSelector; // 文章列表选择器
    private String titleSelector; // 文章标题选择器
    private String contentSelector; // 文章内容选择器
     
    private int totalArticleNum = 0; // 当前抓取文章数量
    private int maxArticleNum = 1000; // 最大抓取文章数量
    private boolean status = true; // 状态
     
    private static final long sleepTime = 10 * 1000; // 休眠时间
    private static final int timeoutMillis = 10 * 1000; // 超时时间
     
    private static final String URL_REGEX = "http[s]?://([a-zA-Z0-9]+\\.)?([a-zA-Z0-9]+\\.[a-zA-Z0-9]+)(/|/.+)?"; // URL正则表达式
     
    public static void main(String[] args) {
        Spider spider = new Spider("http://www.acgist.com", "ACGIST动漫",
                "http://www.acgist.com/article/type?id=3001",
                ".page a", ".article li a", ".title", ".articlemsg");
        spider.findArticles();
    }
     
    public Spider() {
    }
 
    /**
     * 初始化爬虫
     * @param domain 主域名
     * @param domainName 网站名称
     * @param firstPage 第一页
     * @param pageSelector 分页选择器
     * @param listSelector 文章列表选择器
     * @param titleSelector 文章标题选择器
     * @param contentSelector 文章内容选择器
     */
    public Spider(String domain, String domainName, String firstPage, String pageSelector, String listSelector, String titleSelector, String contentSelector) {
        this.domain = domain;
        this.domainName = domainName;
        this.firstPage = firstPage;
        this.listSelector = listSelector;
        this.pageSelector = pageSelector;
        this.titleSelector = titleSelector;
        this.contentSelector = contentSelector;
    }
 
    /**
     * 查找文章
     * @return 文章
     */
    public void findArticles() {
        if(StringUtils.isEmpty(this.domain) || StringUtils.isEmpty(this.firstPage))
            throw new RuntimeException("主域名和第一页地址不能为空");
        if(!this.domain.matches(URL_REGEX) || !this.firstPage.matches(URL_REGEX))
            throw new RuntimeException("主域名和第一页地址不正确:domain:" + this.domain + "----firstPage:" + this.firstPage);
         
        Document document;
        try {
            document = Jsoup.parse(new URL(firstPage), timeoutMillis);
            pagePageHandle(document);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         
    }
 
    /**
     * 获取分页信息
     * @param document 第一页的文档
     * @throws IOException 异常
     * @throws MalformedURLException 异常 
     * @throws InterruptedException 异常
     */
    private void pagePageHandle(Document document) throws MalformedURLException, IOException, InterruptedException {
        List<String> pageUrls = new ArrayList<String>();
        pageUrls.add(firstPage); // 第一页
         
        Elements elements = document.select(pageSelector);
        for (Element element : elements) {
            String href = null;
            if(element.hasAttr("href"))
                href = element.attr("href");
            if(StringUtils.isNotEmpty(href)) {
                if(!href.matches(URL_REGEX))
                    href = domain + href;
                if(href.matches(URL_REGEX) && !pageUrls.contains(href))
                    pageUrls.add(href);
            }
        }
        for (String pageUrl : pageUrls) {
            if(!status)
                return;
            articleListHandle(pageUrl);
            Thread.sleep(sleepTime);
        }
    }
     
    /**
     * 处理文章列表
     * @param pageUrl 当前文章url
     * @throws MalformedURLException 异常
     * @throws IOException 异常
     */
    private void articleListHandle(String pageUrl) throws MalformedURLException, IOException {
        List<String> articleUrls = new ArrayList<String>();
        Document document = Jsoup.parse(new URL(pageUrl), timeoutMillis);
        Elements elements = document.select(listSelector);
        for (Element element : elements) {
            String href = null;
            if(element.hasAttr("href"))
                href = element.attr("href");
            if(StringUtils.isNotEmpty(href)) {
                if(!href.matches(URL_REGEX))
                    href = domain + href;
                if(href.matches(URL_REGEX) && !articleUrls.contains(href))
                    articleUrls.add(href);
            }
        }
        for (String articleUrl : articleUrls) {
            if(!status)
                return;
            articleHandle(articleUrl);
        }
    }
     
    /**
     * 处理文章
     * @param articleUrl 文章列表
     * @throws MalformedURLException 异常
     * @throws IOException 异常
     */
    private void articleHandle(String articleUrl) throws MalformedURLException, IOException {
        Document document = Jsoup.parse(new URL(articleUrl), timeoutMillis);
        Elements titles = document.select(titleSelector);
        Elements contents = document.select(contentSelector);
        if(titles.size() > 0)
            System.out.println(titles.get(0).text());
        if(contents.size() > 0)
            System.out.println(contents.get(0).html());
        totalArticleNum++;
        if(totalArticleNum > maxArticleNum)
            status = false;
        System.out.println("\r\n\r\n\r\n\r\n当前抓取文章列表====" + totalArticleNum + "====\r\n\r\n\r\n\r\n");
    }
 
    //=================getter setter=================//
    public int getMaxArticleNum() {
        return maxArticleNum;
    }
 
    public void setMaxArticleNum(int maxArticleNum) {
        this.maxArticleNum = maxArticleNum;
    }
     
}

代码未大量测试,可能会出现问题。

一些特殊的网站会把其他内容放到文章内容里面,防止一些恶意代码和连接可以使用jsoup过滤。
一些网站的分页连接会使用相对路径。