java如何做高级爬虫

java如何做高级爬虫,第1张

下面说明知乎爬虫的源码和涉及主要技术点:

(1)程序package组织

(2)模拟登录(爬虫主要技术点1)

要爬去需要登录的网站数据,模拟登录是必要可少的一步,而且往往是难点。知乎爬虫的模拟登录可以做一个很好的案例。要实现一个网站的模拟登录,需要两大步骤是:(1)对登录的请求过程进行分析,找到登录的关键请求和步骤,分析工具可以有IE自带(快捷键F12)、Fiddler、HttpWatcher;(2)编写代码模拟登录的过程。

(3)网页下载(爬虫主要技术点2)

模拟登录后,便可下载目标网页html了。知乎爬虫基于HttpClient写了一个网络连接线程池,并且封装了常用的get和post两种网页下载的方法。

(4)自动获取网页编码(爬虫主要技术点3)

自动获取网页编码是确保下载网页html不出现乱码的前提。知乎爬虫中提供方法可以解决绝大部分乱码下载网页乱码问题。

(5)网页解析和提取(爬虫主要技术点4)

使用Java写爬虫,常见的网页解析和提取方法有两种:利用开源Jar包Jsoup和正则。一般来说,Jsoup就可以解决问题,极少出现Jsoup不能解析和提取的情况。Jsoup强大功能,使得解析和提取异常简单。知乎爬虫采用的就是Jsoup。 展开下面说明知乎爬虫的源码和涉及主要技术点:

(1)程序package组织

(2)模拟登录(爬虫主要技术点1)

要爬去需要登录的网站数据,模拟登录是必要可少的一步,而且往往是难点。知乎爬虫的模拟登录可以做一个很好的案例。要实现一个网站的模拟登录,需要两大步骤是:(1)对登录的请求过程进行分析,找到登录的关键请求和步骤,分析工具可以有IE自带(快捷键F12)、Fiddler、HttpWatcher;(2)编写代码模拟登录的过程。

(3)网页下载(爬虫主要技术点2)

模拟登录后,便可下载目标网页html了。知乎爬虫基于HttpClient写了一个网络连接线程池,并且封装了常用的get和post两种网页下载的方法。

(4)自动获取网页编码(爬虫主要技术点3)

自动获取网页编码是确保下载网页html不出现乱码的前提。知乎爬虫中提供方法可以解决绝大部分乱码下载网页乱码问题。

(5)网页解析和提取(爬虫主要技术点4)

使用Java写爬虫,常见的网页解析和提取方法有两种:利用开源Jar包Jsoup和正则。一般来说,Jsoup就可以解决问题,极少出现Jsoup不能解析和提取的情况。Jsoup强大功能,使得解析和提取异常简单。知乎爬虫采用的就是Jsoup。

(6)正则匹配与提取(爬虫主要技术点5)

虽然知乎爬虫采用Jsoup来进行网页解析,但是仍然封装了正则匹配与提取数据的方法,因为正则还可以做其他的事情,如在知乎爬虫中使用正则来进行url地址的过滤和判断。

(7)数据去重(爬虫主要技术点6)

对于爬虫,根据场景不同,可以有不同的去重方案。(1)少量数据,比如几万或者十几万条的情况,使用Map或Set便可;(2)中量数据,比如几百万或者上千万,使用BloomFilter(著名的布隆过滤器)可以解决;(3)大量数据,上亿或者几十亿,Redis可以解决。知乎爬虫给出了BloomFilter的实现,但是采用的Redis进行去重。

(8)设计模式等Java高级编程实践

除了以上爬虫主要的技术点之外,知乎爬虫的实现还涉及多种设计模式,主要有链模式、单例模式、组合模式等,同时还使用了Java反射。除了学习爬虫技术,这对学习设计模式和Java反射机制也是一个不错的案例。

4 一些抓取结果展示收起

下面是源代码,希望可以帮到你~~

package comlymainprocess;

import javaioBufferedReader;

import javaioInputStreamReader;

import javautilArrayList;

import javautilList;

import orgapachehttpConsts;

import orgapachehttpHeader;

import orgapachehttpHttpEntity;

import orgapachehttpHttpResponse;

import orgapachehttpNameValuePair;

import orgapachehttpStatusLine;

import orgapachehttpcliententityUrlEncodedFormEntity;

import orgapachehttpclientmethodsHttpGet;

import orgapachehttpclientmethodsHttpPost;

import orgapachehttpcookieCookie;

import orgapachehttpimplclientDefaultHttpClient;

import orgapachehttpmessageBasicNameValuePair;

import orgapachehttputilEntityUtils;

public class Test1 {

public static void main(String[] args){

Test1 test1 = new Test1();

Systemoutprintln(test1process("",""));

}

@SuppressWarnings("deprecation")

public boolean process(String username,String password) {

boolean ret=false;

DefaultHttpClient httpclient = new DefaultHttpClient();

try {

HttpGet httpget;

HttpResponse response;

HttpEntity entity;

List<Cookie> cookies;

//组建登录的post包

HttpPost httppost = new HttpPost("http://loginhimopcom/Logindo"); // 用户登录

List<NameValuePair> nvps = new ArrayList<NameValuePair>();

nvpsadd(new BasicNameValuePair("nickname", username));

nvpsadd(new BasicNameValuePair("password", password));

nvpsadd(new BasicNameValuePair("origURL", "http://himopcom/SysHomedo"));

nvpsadd(new BasicNameValuePair("loginregFrom", "index"));

nvpsadd(new BasicNameValuePair("ss", "10101"));

httppostsetEntity(new UrlEncodedFormEntity(nvps, ConstsUTF_8));

httppostaddHeader("Referer", "http://himopcom/SysHomedo");

httppostaddHeader("Connection", "keep-alive");

httppostaddHeader("Content-Type", "application/x-www-form-urlencoded");

httppostaddHeader("Accept-Language", "zh-CN,zh;q=08");

httppostaddHeader("Origin", "http://himopcom");

httppostaddHeader("User-Agent", "Mozilla/50 (Windows NT 61; WOW64) AppleWebKit/53736 (KHTML, like Gecko) Chrome/3001599101 Safari/53736");

response = httpclientexecute(httppost);

entity = responsegetEntity();

// Systemoutprintln("Login form get: " + responsegetStatusLine());

EntityUtilsconsume(entity);

// Systemoutprintln("Post logon cookies:");

cookies = httpclientgetCookieStore()getCookies();

if (cookiesisEmpty()) {

// Systemoutprintln("None");

} else {

for (int i = 0; i < cookiessize(); i++) {

// Systemoutprintln("- " + cookiesget(i)toString());

}

}

//进行页面跳转

String url = ""; // 页面跳转

Header locationHeader = responsegetFirstHeader("Location");

// Systemoutprintln(locationHeadergetValue());

if (locationHeader != null) {

url = locationHeadergetValue(); // 得到跳转href

HttpGet httpget1 = new HttpGet(url);

response = httpclientexecute(httpget1);

// 登陆成功。。。hoho

}

entity = responsegetEntity();

// Systemoutprintln(responsegetStatusLine());

if (entity != null) {

// Systemoutprintln("Response content length: " + entitygetContentLength());

}

// 显示结果

BufferedReader reader = new BufferedReader(new InputStreamReader(entitygetContent(), "UTF-8"));

String line = null;

while ((line = readerreadLine()) != null) {

// Systemoutprintln(line);

}

//自动打卡

// 访问网站的子网页。

HttpPost httppost1 = new HttpPost("http://homehimopcom/ajaxGetContinusLoginAwarddo"); // 设置个人信息页面

httppost1addHeader("Content-Type", "text/plain;charset=UTF-8");

httppost1addHeader("Accept", "text/plain, /");

httppost1addHeader("X-Requested-With", "XMLHttpRequest");

httppost1addHeader("Referer", "http://homehimopcom/Homedo");

response = httpclientexecute(httppost1);

entity = responsegetEntity();

// Systemoutprintln(responsegetStatusLine());

if(responsegetStatusLine()toString()indexOf("HTTP/11 200 OK")>=0){

ret = true;

}

if (entity != null) {

// Systemoutprintln("Response content length: " + entitygetContentLength());

}

// 显示结果

reader = new BufferedReader(new InputStreamReader(entitygetContent(), "UTF-8"));

line = null;

while ((line = readerreadLine()) != null) {

Systemoutprintln(line);

}

} catch (Exception e) {

} finally {

httpclientgetConnectionManager()shutdown();

}

return ret;

}

}

//Java爬虫demo

 

import javaioFile;

import javanetURL;

import javanetURLConnection;

import javaniofileFiles;

import javaniofilePaths;

import javautilScanner;

import javautilUUID;

import javautilregexMatcher;

import javautilregexPattern;

 

public class DownMM {

    public static void main(String[] args) throws Exception {

        //out为输出的路径,注意要以\\结尾

        String out = "D:\\JSP\\pic\\java\\"; 

        try{

            File f = new File(out);

            if(! fexists()) {  

                fmkdirs();  

            }  

        }catch(Exception e){

            Systemoutprintln("no");

        }

        

        String url = "http://wwwmzitucom/share/comment-page-";

        Pattern reg = Patterncompile("<img src=\"()\"");

        for(int j=0, i=1; i<=10; i++){

            URL uu = new URL(url+i);

            URLConnection conn = uuopenConnection();

            connsetRequestProperty("User-Agent", "Mozilla/50 (Windows NT 63; WOW64; Trident/70; rv:110) like Gecko");

            Scanner sc = new Scanner(conngetInputStream());

            Matcher m = regmatcher(scuseDelimiter("\\A")next());

            while(mfind()){

                Filescopy(new URL(mgroup(1))openStream(), Pathsget(out + UUIDrandomUUID() + "jpg"));

                Systemoutprintln("已下载:"+j++);

            }

        }

    }

}

一、需求

    1定时抓取固定网站新闻标题、内容、发表时间和来源。

    2程序需要支持分布式、多线程

 

二、设计

   1网站是固定,但是未来也可能添加新的网站去抓取,每个网站内容节点设计都不一样,这样就需要支持动态可配置来新增网站以方便未来的扩展,这样就需要每次都需要开发介入。

    2网站html节点的结构可能发生变化,所以也要支持提取节点可配置。

    3怎样支持分布式?暂时最简单的想法就是:多机器部署程序,还有新搞一台或者部署程序其中一台制作一个定时任务,定时开启每台机器应该抓取哪个网站,暂时不能支持同一个网站同时可以支持被多台机器同时抓取,这样会比较麻烦,要用到分布式队列。所以暂时一个网站同时只会被单台机器抓取。

    4多线程,怎样多线程?多线程抓取我这边有两个实现:

        (1)一个线程抓取一个网站,维护一个自己的url队列做广度抓取,同时抓取多个网站。如图:

           

        (2)多个线程同时抓取不同的网站。如图:

       以上两张办法其实各有优点,也给有缺点,看我们怎么取舍了。

       方法1:每个线程创建一个自己的队列,图中的queue可以不用concurrentQueue,优点:不涉及到控制并发,每个网站一个线程抓取一个网站,抓取完毕即自动回收销毁线程。控制方便。缺点:线程数不可以扩展,例如当只有3个网站,你最多只能开3个线程来抓取,不能开更多,有一定的局限性。

       方法2:N个线程同时抓取N个网站,线程数和网站数目不挂钩,优点:线程数可以调整并且和和抓取网站数量无关。3个网站我们可以开4个5个或者10个这个可以根据您的硬件资源进行调整。缺点:需要控制并发,并且要控制什么时候销毁线程(thread1空闲,并且queue为空不代表任务可以结束,可能thread2结果还没返回),当被抓取的网站响应较慢时,会拖慢整个爬虫进度。

 

三、实现

    抓取方式最终还是选择了方法二,因为线程数可配置!

     使用技术:

          jfinal用了之后才发现这东西不适合,但是由于项目进度问题,还是使用了。

          maven项目管理

          jettyserver

          mysql

          eclipse开发

     项目需要重点攻破的难点:

          (1)合理的控制N个线程正常的抓取网站,并且当所有线程工作都完成了并且需要抓取的队列为空时,N个线程同时退出销毁。

          (2)不同网站设计节点不一样,需要通过配置解决各个网站需要抓取的URL和抓取节点内容在html节点的位置。

          (3)个性化内容处理,由于html结构设计问题,北大青鸟认为抓取的内容可能有些多余的html标签,或者多余的内容该怎么处理。

Java爬虫框架WebMagic简介及使用

一、介绍

webmagic的是一个无须配置、便于二次开发的爬虫框架,它提供简单灵活的API,只需少量代码即可实现一个爬虫。webmagic采用完全模块化的设计,功能覆盖整个爬虫的生命周期(链接提取、页面下载、内容抽取、持久化),支持多线程抓取,分布式抓取,并支持自动重试、自定义UA/cookie等功能。

二、概览

WebMagic项目代码分为核心和扩展两部分。核心部分(webmagic-core)是一个精简的、模块化的爬虫实现,而扩展部分则包括一些便利的、实用性的功能(例如注解模式编写爬虫等)。

WebMagic的结构分为Downloader、PageProcessor、Scheduler、Pipeline四大组件,并由Spider将它们彼此组织起来。这四大组件对应爬虫生命周期中的下载、处理、管理和持久化等功能。而Spider则将这几个组件组织起来,让它们可以互相交互,流程化的执行,可以认为Spider是一个大的容器,它也是WebMagic逻辑的核心。

21 WebMagic的四个组件

Downloader

Downloader负责从互联网上下载页面,以便后续处理。WebMagic默认使用了Apache HttpClient作为下载工具。

PageProcessor

PageProcessor负责解析页面,抽取有用信息,以及发现新的链接。WebMagic使用Jsoup作为HTML解析工具,并基于其开发了解析XPath的工具Xsoup。在这四个组件中,PageProcessor对于每个站点每个页面都不一样,是需要使用者定制的部分。

Scheduler

Scheduler负责管理待抓取的URL,以及一些去重的工作。WebMagic默认提供了JDK的内存队列来管理URL,并用集合来进行去重。也支持使用Redis进行分布式管理。除非项目有一些特殊的分布式需求,否则无需自己定制Scheduler。

Pipeline

Pipeline负责抽取结果的处理,包括计算、持久化到文件、数据库等。WebMagic默认提供了“输出到控制台”和“保存到文件”两种结果处理方案。Pipeline定义了结果保存的方式,如果你要保存到指定数据库,则需要编写对应的Pipeline。对于一类需求一般只需编写一个Pipeline。

22 用于数据流转的对象

Request

Request是对URL地址的一层封装,一个Request对应一个URL地址。它是PageProcessor与Downloader交互的载体,也是PageProcessor控制Downloader唯一方式。

Page

Page代表了从Downloader下载到的一个页面——可能是HTML,也可能是JSON或者其他文本格式的内容。Page是WebMagic抽取过程的核心对象,它提供一些方法可供抽取、结果保存等。

ReusltItems

ReusltItems相当于一个Map,它保存PageProcessor处理的结果,供Pipeline使用。它的API与Map很类似,值得注意的是它有一个字段skip,若设置为true,则不应被Pipeline处理。

23 控制爬虫运转的引擎—Spider

Spider是WebMagic内部流程的核心。Downloader、PageProcessor、Scheduler、Pipeline都是Spider的一个属性,这些属性是可以自由设置的,通过设置这个属性可以实现不同的功能。Spider也是WebMagic *** 作的入口,它封装了爬虫的创建、启动、停止、多线程等功能。

对于编写一个爬虫,PageProcessor是需要编写的部分,而Spider则是创建和控制爬虫的入口。

24 WebMagic项目组成

WebMagic项目代码包括几个部分,在根目录下以不同目录名分开。它们都是独立的Maven项目。

WebMagic主要包括两个包,这两个包经过广泛实用,已经比较成熟:

webmagic-core

webmagic-core是WebMagic核心部分,只包含爬虫基本模块和基本抽取器。

webmagic-extension

webmagic-extension是WebMagic的主要扩展模块,提供一些更方便的编写爬虫的工具。包括注解格式定义爬虫、JSON、分布式等支持。

三、 基本的爬虫

31 爬虫的流程 (可以参考上边的框架架构图)

Downloader-页面下载

页面下载是一切爬虫的开始。

大部分爬虫都是通过模拟http请求,接收并分析响应来完成。这方面,JDK自带的HttpURLConnection可以满足最简单的需要,而Apache HttpClient(40后整合到HttpCompenent项目中)则是开发复杂爬虫的不二之选。它支持自定义HTTP头(对于爬虫比较有用的就是User-agent、cookie等)、自动redirect、连接复用、cookie保留、设置代理等诸多强大的功能。

webmagic使用了HttpClient 42,并封装到了HttpClientDownloader。学习HttpClient的使用对于构建高性能爬虫是非常有帮助的,官方的Tutorial就是很好的学习资料。目前webmagic对HttpClient的使用仍在初步阶段,不过对于一般抓取任务,已经够用了

PageProcessor-页面分析及链接抽取

Selector是webmagic为了简化页面抽取开发的独立模块,是整个项目中我最得意的部分。这里整合了CSS Selector、XPath和正则表达式,并可以进行链式的抽取,很容易就实现强大的功能。即使你使用自己开发的爬虫工具,webmagic的Selector仍然值得一试

Jsoup

HtmlParser

Apache tika

HtmlCleaner与Xpath

这里说的页面分析主要指HTML页面的分析。页面分析可以说是垂直爬虫最复杂的一部分,在webmagic里,PageProcessor是定制爬虫的核心。通过编写一个实现PageProcessor接口的类,就可以定制一个自己的爬虫

HTML分析是一个比较复杂的工作,Java世界主要有几款比较方便的分析工具:

webmagic的Selector

Scheduler-URL管理

URL管理的问题可大可小。对于小规模的抓取,URL管理是很简单的。我们只需要将待抓取URL和已抓取URL分开保存,并进行去重即可。使用JDK内置的集合类型Set、List或者Queue都可以满足需要。如果我们要进行多线程抓取,则可以选择线程安全的容器,例如LinkedBlockingQueue以及ConcurrentHashMap。因为小规模的URL管理非常简单,很多框架都并不将其抽象为一个模块,而是直接融入到代码中。但是实际上,抽象出Scheduler模块,会使得框架的解耦程度上升一个档次,并非常容易进行横向扩展,这也是我从scrapy中学到的。

Pipeline-离线处理和持久化

Pipeline其实也是容易被忽略的一部分。大家都知道持久化的重要性,但是很多框架都选择直接在页面抽取的时候将持久化一起完成,例如crawer4j。但是Pipeline真正的好处是,将页面的在线分析和离线处理拆分开来,可以在一些线程里进行下载,另一些线程里进行处理和持久化。

32 使用WebMagic爬取一个壁纸网站

首先引入WebMagic的依赖,webmagic-core-{version}jar和webmagic-extension-{version}jar。在项目中添加这两个包的依赖,即可使用WebMagic。

maven中引入依赖jar包

<dependency>

   <groupId>uscodecraft</groupId>

   <artifactId>webmagic-core</artifactId>

   <version>053</version>

</dependency>

<dependency>

   <groupId>uscodecraft</groupId>

   <artifactId>webmagic-extension</artifactId>

   <version>053</version>

</dependency>1234567891012345678910

不使用maven的用户,可以去http://webmagicio中下载最新的jar包。

没必要,做线程。

做线程的思想是为了让你 支持多个页面一起爬,

单独的HTTP 请求就可以,

其实HTTP,你请求了,它就会把页面给你,然后你用IO流读取下来,

然后用正则 或者 replace 获取到自己 用的代码就 OK 了

1、在打开的ie浏览器窗口右上方点击齿轮图标,选择“Internet选项”,如下图所示:

2、在打开的Internet选项窗口中,切换到安全栏,在安全选卡中点击“自定义级别”,如下图所示:

3、在“安全设置-Internet 区域”界面找到“Java 小程序脚本”、“活动脚本”,并将这两个选项都选择为“禁用”,然后点击确定,如下图所示:

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zaji/13492176.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2025-09-01
下一篇2025-09-01

发表评论

登录后才能评论

评论列表(0条)

    保存