
下面说明知乎爬虫的源码和涉及主要技术点:
(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 小程序脚本”、“活动脚本”,并将这两个选项都选择为“禁用”,然后点击确定,如下图所示:
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)