基于Java的网页爬虫实践


本文项目san-spider源码地址
https://github.com/lufei222/san-spider.git


爬虫概念

1、爬虫基本概念

爬虫的概念 :网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。这是百度百科对爬虫的定义,其实,说简单点,爬虫就是利用写好的程序自动的提取网页的信息。

2、爬虫的分类
通用爬虫:通用爬虫是搜索引擎(Baidu、Google、Yahoo等)“抓取系统”的重要组成部分。主要目的是将互联网上的网页下载到本地,形成一个互联网内容的镜像备份。 简单来讲就是尽可能的;把互联网上的所有的网页下载下来,放到本地服务器里形成备分,在对这些网页做相关处理(提取关键字、去掉广告),最后提供一个用户检索接口。
聚焦爬虫:聚焦爬虫是根据指定的需求抓取网络上指定的数据。例如:获取豆瓣上电影的名称和影评,而不是获取整张页面中所有的数据值。
增量式爬虫:增量式是用来检测网站数据更新的情况,且可以将网站更新的数据进行爬取。

3、爬虫的价值
抓取互联网上的数据,为我所用,有了大量的数据,就如同有了一个数据银行一样,下一步做的就是如何将这些爬取的数据产品化,商业化。
在这里插入图片描述


愿景

开源的爬虫框架已经很多了,有各种语言(比如:python、java)实现的,有单机的,还有大型分布式的,多达上百种,详情可见:
开源中国网络爬虫框架列表
33款可用来抓数据的开源爬虫软件工具
爬虫项目经验小结
github上有哪些优秀的java爬虫项目

我们的要求也不高:

  • 社区丰富,用户量多,多人用的项目少踩坑。
  • 文档要全,上手快。
  • 安装和集成使用简单。
  • 代码语法简洁
  • 爬虫的主要功能要有,比如:支持请求头部设置,支持代理,支持多线程,url自动去重复,html解析方便(至少要能支持css选择器,xpath选择器,正则表达式等常见的解析方式)

选定好一款爬虫开源框架后,就要考虑自己的业务特点,设计自己的项目架构了,大多数用爬虫的人,基本需求其实是类似的。

最终一般的爬虫项目都是这样的操作:

  1. 将目标网站的页面尽可能快速的扒下来

  2. 然后解析出有用的内容

  3. 落地存储到db、缓存

稍微成熟爬虫开源框架基本上都已经实现了第一步 。
根据实际业务规则解析完了以后,如何落地、保持更新网站变更策略,都需要我们去考虑。


爬虫框架选型

可以参考 开源网络爬虫框架应该怎么选?
考虑选型的时候主要有以下参考项:

  • 支持多线程?
  • 爬虫能用代理么?
  • 爬虫会爬取重复数据么?
  • 爬虫能爬取ajax生成的信息么?
  • 爬虫怎么抽取网页信息
  • 爬虫怎么保存网页的信息
  • 爬虫速度如何?
  • 报错容易定位修改吗?

上面说的爬虫,基本可以分3类:

  • 分布式爬虫:Nutch…
  • JAVA单机爬虫:Crawler4j、WebMagic、WebCollector、Gecco、Jsoup、Htmlunit
  • 非JAVA单机爬虫:Scrapy…

分布式爬虫

Nucth:
优点:分布式抓取,存储和索引,有hadoop支持,第三方插件丰富
缺点:使用上手难,用Nutch进行爬虫的二次开发,爬虫的编写和调试所需的时间,往往是单机爬虫所需的十倍时间不止。

单机爬虫

对于单机爬虫框架,日常开发中占用时间多的地方就是网页内容解析,所以首先要介绍下优秀的HTML网页解析器 :Jsoup和Htmlunit和神器Selenium

  • Jousp: 一款非常流行的Java的HTML解析器,主要用来对HTML解析。也可以进行Http请求网页爬取源码。
  • Htmlunit:它不仅仅是一个HTML解析器。这是一个真正的“无GUI浏览器”和HTML单元测试工具。
  • Selenium:Selenium是基于Web应用的验收测试工具集合,直接运行在浏览器中,通过一系列命令来模拟用户操作,Selenium可以将这些命令转化成实际的HTTP请求在浏览器中运行 ,获取到网页信息和Cookie等信息

上面列举的单机爬虫中,Gecco基于注解方式实现,官方demo无法运行,体验太差,首先排除不考虑。

对于其他几个,功能都很丰富,且都在持续更新中。

nameGithub Star文档丰富度使用项目数网络博文丰富度(10)
Crawler4j3.9k51996
WebMagic9.1k非常齐全5867.5
WebCollector2.6k7727.5

从以上几个指标来看,都很优秀
基于我实际项目运行对比情,WebMagic文档丰富上手快,demo项目多,所以当前日常使用WebMagic。


非Java单机爬虫

主要说 Python 爬虫,以Scrapy为首,对比Java主要优势在于

  • Python的语法简洁 ,入门简单,节点解析简洁高效,比如:Python 可以用 30 行代码,完成 Java 50 行代码干的任务。Python 写代码的确快。
  • 爬虫用户量大、社区活跃度大。
  • Github基于Python实践的项目基数巨大,对于大多数网站,基本有参考,拿来修改即用。

综上对比:
分布式爬虫Nucth有点大材小用,开发效率也不高,暂时不打算考虑。
在日常Java项目中,我会首选WebMagic,而当需要花费大量时间精力去做爬虫工作的时候,我会选择Python的Scrapy。


爬虫和反爬虫

如何应对网站反爬虫策略?如何高效地爬大量数据?
爬虫突破封禁的6种常见方法
常见反爬虫机制与应对方法
爬虫与反爬虫的博弈

爬虫和反爬虫都是一直在进步的,下面列举一些常见的爬与反爬运用涉及的相关知识点

  • 设置User-Agent
  • 设置Cookie
  • 访问频率限制
  • 代理IP或者分布式爬虫:
  • 构造合理的HTTP请求头

网页节点的解析方式

CSS选择器 + Xpath + 正则表达式整理
xpath表达式


Jsoup、WebCollector、Htmlunit解析实例

Jsoup源码
WebCollector源码
接下来大篇幅主要介绍Webmagic,因为先简要介绍下其他解析器。
使HttpClient、Jsoup、Htmlunit爬取网页实例代码源码:
LagouMulSpider.java

package demo;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.util.List;
import java.util.Random;

/**
 * lagou多种不同方式实现的爬虫
 * 测试htmlunit、jsoup、httpclient直接爬取
 */
public class LagouMulSpider {

    static Random random = new Random();
    private static String LAGOU_URL = "https://www.lagou.com/zhaopin/ceo/1/?filterOption=3&sid=f1937baf1115438c9ea9aee62836a985";


    public static void main(String[] args) throws IOException, InterruptedException {
        //直接httpclient爬取lagou网页,会提示存在恶意访问行为被拦截,
        testHttpClient();
        testJsoup();
        testHtmlunitLagou();
        testHtmlunitBaidu();
    }
    private static void testJsoup() throws IOException, InterruptedException {
        System.out.println("************************testJsoup************************");
        for(int i=0;i<2;i++) {
            Document doc = Jsoup.connect(LAGOU_URL).get();
            Elements newsHeadlines = doc.select(".pager_container");
            System.out.println(newsHeadlines);
            Thread.sleep(200);
        }
    }


    private static void testHttpClient() {
        try {
            System.out.println("************************testHttpClient************************");
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpGet httpGet = new HttpGet(LAGOU_URL);

            CloseableHttpResponse response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            String content = EntityUtils.toString(entity, "utf-8");
            response.close();
            Jsoup.parse(content);
            Document doc = Jsoup.parse(content);
            Elements elements = doc.getElementsByTag("title");
            System.out.println(elements);
        }catch (Exception e){
            System.out.println(e.getCause());
        }
    }

    private static BrowserVersion getRandomBrowserVersion(){
        int i = random.nextInt(5);
        BrowserVersion browserVersion = BrowserVersion.getDefault();
        switch (i){
            case 1:  browserVersion = BrowserVersion.CHROME  ;break;
            case 2:  browserVersion = BrowserVersion.FIREFOX  ;break;
            case 3:  browserVersion = BrowserVersion.FIREFOX_68  ;break;
            case 4:  browserVersion = BrowserVersion.BEST_SUPPORTED  ;break;
            case 5:  browserVersion = BrowserVersion.INTERNET_EXPLORER  ;break;
            default: ;
        }
        return browserVersion;
    }

    /**
     * 测试循环获取lagou的页面是否也是五次限制,还是真的能像浏览器一样正常访问.发现其实是一样限流了,超过32秒才能继续访问
     * @throws IOException
     * @throws InterruptedException
     */
    private static void testHtmlunitLagou() throws IOException, InterruptedException {
        System.out.println("************************testHtmlunitLagou************************");
        for(int i=0;i<2;i++){
            //创建一个webclient
            WebClient webClient = new WebClient(getRandomBrowserVersion());
            //htmlunit 对css和javascript的支持不好,所以请关闭之
            webClient.getOptions().setJavaScriptEnabled(false);
            webClient.getOptions().setCssEnabled(false);
            //获取页面
            HtmlPage page = webClient.getPage(LAGOU_URL);
            List<Object> byXPath = page.getByXPath("//div[@class='pager_container']//text()");
            System.out.println(byXPath);
            //关闭webclient
            webClient.close();
        }
    }
    private static void testHtmlunitBaidu() throws IOException {
        System.out.println("************************testHtmlunitBaidu************************");
        String str;
        //创建一个webclient
        WebClient webClient = new WebClient();
        //htmlunit 对css和javascript的支持不好,所以请关闭之
        webClient.getOptions().setJavaScriptEnabled(false);
        webClient.getOptions().setCssEnabled(false);
        //获取页面
        HtmlPage page = webClient.getPage("http://www.baidu.com/");
        //获取页面的TITLE
        str = page.getTitleText();
        System.out.println(str);
        webClient.close();
    }
}

WebCollector解析实例


WebMagic的介绍及使用

Java爬虫框架WebMagic的介绍及使用(定时任务、代理)
官方文档
Java爬虫框架WebMagic入门

1、WebMagic框架简介

PageProcessor、Scheduler、Downloader和Pipeline,对应爬虫生命周期中的处理、管理、下载和持久化等功能,都是Spider中的属性,爬虫框架通过Spider启动和管理。

WebMagic总体架构图如下:
在这里插入图片描述

2、四大组件

  • PageProcessor 负责解析页面,抽取有用信息,以及发现新的链接。需要自己定义。
  • Scheduler 负责管理待抓取的URL,以及一些去重的工作。一般无需自己定制Scheduler。
  • Pipeline 负责抽取结果的处理,包括计算、持久化到文件、数据库等。
  • Downloader 负责从互联网上下载页面,以便后续处理。一般无需自己实现。

3、用于数据流转的对象
Request 是对URL地址的一层封装,一个Request对应一个URL地址。
Page 代表了从Downloader下载到的一个页面——可能是HTML,也可能是JSON或者其他文本格式的内容。
ResultItems 相当于一个Map,它保存PageProcessor处理的结果,供Pipeline使用。


WebMagic+Selenium自动化登录爬虫实践

WebMagic其他 demo
WebMagic实例 GiteeAutoLoginSpider.java

视频展示效果如下
https://share.weiyun.com/liXqrw51
https://weibo.com/tv/v/J7HTMa0Zu?fid=1034:4518366212194347

package demo;
 
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
 
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.pipeline.ConsolePipeline;
import us.codecraft.webmagic.pipeline.FilePipeline;
import us.codecraft.webmagic.processor.PageProcessor;


/**
 * gitee自动化登录获取私有项目
 */
public class GiteeAutoLoginSpider implements PageProcessor {

	private static String GITEE_NAME = System.getenv("GITEE_NAME");
	private static String GITEE_USERNAME = System.getenv("GITEE_USERNAME");
	private static String GITEE_PASSWORD = System.getenv("GITEE_PASSWORD");
	private static String GITEE_URL = "https://gitee.com/login";


	private Site site = Site.me().setRetryTimes(3).setSleepTime(1000).setTimeOut(10000);

	// 用来存储cookie信息
	private Set<Cookie> cookies = new HashSet<>();

	@Override
	public Site getSite() {
		// 将获取到的cookie信息添加到webmagic中
		for (Cookie cookie : cookies) {
			site.addCookie(cookie.getName(), cookie.getValue());
		}
		return site;
	}

	/**
	 * 解析网页节点具体业务逻辑
	 * @param page
	 */
	@Override
	public void process(Page page) {

		System.out.println("开始解析");
		String  tabName = page.getHtml().xpath("//a[@class='item f-bold']//allText()").get();
		System.out.println(tabName);
		List<String> projects = page.getHtml().xpath("//span[@class='project-title']//allText()").all();
		List<String> privateProject = projects.stream().filter(x -> x.contains("san")).distinct().collect(Collectors.toList());
		System.out.println(privateProject);
		page.putField("gitee project ", privateProject);

	}
	
    /**
	 * 登录获取cookie的操作
	 * 
     * 使用selenium+chromedriver驱动完成自动登录gitee获取cookie的操作
     * 对于大多数网站可以直接获得cookie
     * 对于大型的验证比较多的网站,会比较麻烦,建议可以百度 或者 github参照其他项目的selenium自动登录实现
     * 在自动登录实现不可行的时候,更快的方式是直接浏览器登录手动复制cookie,以便后续登录之后的操作继续正常进行
     */
	public void login() {
		// 登陆
		System.setProperty("webdriver.chrome.driver", "D:/chromedriver/chromedriver.exe"); // 注册驱动
		WebDriver driver = new ChromeDriver();
		driver.get(GITEE_URL);// 打开网址
		// 防止页面未能及时加载出来而设置一段时间延迟
		try {
			Thread.sleep(1000);
			// 设置用户名密码
			driver.findElement(By.id("user_login")).sendKeys(GITEE_USERNAME); // 用户名
			driver.findElement(By.id("user_password")).sendKeys(GITEE_PASSWORD); // 密码
			// 模拟点击
			driver.findElement(By.name("commit")).click();
			// 防止页面未能及时加载出来而设置一段时间延迟
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 获取cookie信息
		cookies = driver.manage().getCookies();
		System.out.println("cookie " + cookies);

		driver.close();

	}
 
	public static void main(String[] args) {
 
		String url = "https://gitee.com/"+GITEE_NAME+"/dashboard/projects?scope=private&&sort="; // 地址
		GiteeAutoLoginSpider dome = new GiteeAutoLoginSpider();
        // 登陆
		dome.login();
		Spider.create(dome)
				.addUrl(url)
				//输出内容到控制台
				.addPipeline(new ConsolePipeline())
				//输出内容到文件
				.addPipeline(new FilePipeline("D:\\webmagic\\gitee"))
				.run();

	}
}

结论和参考

经过项目的经验时间,其实上面列举的功能都很强大,如果只是Java项目当中由于一些需求需要使用,那么其实Jsoup或者Htmlunit足矣,要有代理、多线程、去重、头部设置、自动登录等,则根据自己需要引入WebMagic或Selenium等,参照着Github上面的丰富的爬虫项目肯定能完成自己的需求。
如果你需要投入大量时间精力在爬虫上面的话,建议直接用Python的Scrapy,已有开源项目足矣让你在爬虫工作上游刃有余。

本文项目san-spider源码地址
https://github.com/lufei222/san-spider.git


参考

口罩地址Python
htmlunit 爬虫案例
实例二 htmlunit
解决htmlunit的webclient对象在多线程环境下的共享问题
高级爬虫进阶:HtmlUnit+多线线程+消息队列快速抓取大量信息数据
Java爬虫,爬取京东、天猫、淘宝、阿里巴巴、苏宁、国美、考拉电商数据
基于webmagic的爬虫项目经验小结
CSS选择器 + Xpath + 正则表达式整理
在开源中国爬虫分类的软件
雪球网的爬虫
今日头条相关的httpunit
Nutch、heritrix、crawler4j优缺点
关于webmagic的说明文档
基于Webmagic的Java爬虫(四)爬取动态列表页内容
基于webmagic的理财产品分页
java爬虫Gecco工具抓取新闻实例
爬虫京东
java爬虫事例
github上有哪些优秀的java爬虫项目
https://www.zhihu.com/question/31427895
https://www.52pojie.cn/thread-1068214-1-1.html
使用WebMagic多线程爬取图+httpClient多线程下载图片
拉勾网爬取(WebMagic+Selenium+ChromeDriver)
https://blog.csdn.net/weixin_43719622/article/details/102784141
https://github.com/Yangtze-Innovation/Search-Job-Platfom/tree/CourageHe/2-WebMagic/4-WebMagicSelenimu
第一次用webmagic写爬虫
webmagic简书
爬取动态页面模拟登录
WebMagic 实现爬虫入门教程
爬取页面需要登陆才可爬取,这种怎么解决
不能登录的常见问题1
不能登录的常见问题2
webmagic框架图
WebMagic实现分布式抓取以及断点抓取,爬虫主要运行时间消耗是请求网页时的io阻塞,所以开启多线程,让不同请求的等待同时进行,可以大大提高爬虫运行效率
多线程爬虫图
Gather Platform 聚集收集平台
基于Crawler4j + jsoup实现虫
selenium介绍
crawler4j简介
htmlunit HtmlUnit的使用
webcollector简介
Java开源爬虫框架WebCollector爬取CSDN博客
爬取微信公众号
Java开源爬虫框架WebCollector爬取搜索引擎
爬虫与反爬虫

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值