网站内容采集系统(如何爬数据css需求数据采集系统:一个能够经过配置规则)
优采云 发布时间: 2022-02-04 11:07网站内容采集系统(如何爬数据css需求数据采集系统:一个能够经过配置规则)
记录两年前写的一个采集系统,包括需求、分析、设计、实现、遇到的问题和系统结果。系统的主要功能是能够对每个网站进行不同的操作。采集规则被配置为对每个网站进行数据爬取。两年前我离开的时候爬取的数据量大概是几千万,每天采集的数据增量大概10000条左右,有1200多条网站配置采集,现在记录系统实现,并提供一些简单的爬虫demo供大家学习如何爬取数据css
需要
数据采集系统:可以配置规则的系统采集不同网站主要目标:html
根据不同的网站,通过配置不同的采集规则来实现网页数据的抓取。对于每一个内容,可以定时抽取特征数据来爬取网站采集配置规则可以维护采集入站数据可以维护架构图的所有数据
数据采集系统架构图java
分析
第一步是先分析需求,所以提取系统的主要需求:jquery
根据不同的网站,可以通过不同的采集规则爬取数据。对于每个内容,可以提取特征数据。特征数据是指标题、作者和发布时间。信息定时任务与任务或任务组关联爬取网站的数据
我们来分析一下网站的结构,无外乎两种;网络
一是列表页。这里的列表页表示需要在当前页面获取更多详情页面的网页链接类型。和通常的查询列表一样,通过列表可以获得更多的详情页链接。一是详情页,比较容易理解。这种页面不需要连接到本页面上的其他网页,可以直接从当前页面中提取数据。
基本上所有爬取的网站都可以这样抽象。阿贾克斯
设计
为分析结果设计一个实现:正则表达式
任务表redis
每个 网站 都可以被视为一个任务来实现 采集 设计模式
两个规则表浏览器
每个 网站 对应于它自己的 采集 规则。根据上面分析的网站结构,采集规则可以细分为两张表,一张收录网站connect获取详情页列表列表采集规则table,网站详情页详情采集规则表的特征数据采集的规则表
网址表
负责记录采集target网站的详情页的url
计划任务表
根据定时任务定时执行某些任务(可以使用定时任务关联多个任务,也可以考虑添加任务组表,定时任务关联任务组,任务组关联任务)
数据存储表
这是因为我们的采集的数据主要是投标和中标两种数据,并且建立了两个表用于数据存储,中标信息表和中标信息表。
实施框架
基础设施是:ssm+redis+htmlunit+jsoup+es+mq+quartz 有很多java可以实现爬虫的框架,htmlunit,WebMagic,jsoup等,有很多优秀的开源框架,当然httpclient也可以实施的。
为什么要使用 htmlunit?htmlunit 是一个开源的java页面分析工具。阅读完页面后,可以有效的使用htmlunit对页面内容进行分析。该项目可以模拟浏览器的操作,号称java浏览器的开源实现
简单说说我对htmlunit的理解:
一是htmlunit提供了通过xpath定位页面元素的功能,通过xpath可以提取页面的特征数据;二是对js的支持,这意味着你真的可以把它当做浏览器来使用。可以用它来模拟点击、输入、登录等操作,对于采集,支持js可以解决页面使用ajax获取数据的问题。当然,htmlunit也支持代理ip、https,配置后可以模拟google、firefox等浏览器、referer、user-agent,是否加载js、css,是否支持ajax等。
XPath 语法是 XML 路径语言,它是一种用于确定 XML 文档的某个部分的位置的语言。
为什么要使用 jsoup?与htmlunit相比,jsoup提供了类似于jquery选择器的定位页面元素的功能,两者可以互补使用。
采集
采集数据逻辑分为两部分:url采集器、详情页采集器
网址采集器:
详情页采集器:
遇到问题的去重:在采集url的时候进行去重,对url进行去重。将key作为url存储在redis中后,缓存时间为3天。这个方法是为了防止同一个url重复采集。标题去重后,key为采集的标题存储在redis中,缓存时间为3天。此方法是为了防止 文章 被不同的 网站 发布,重复 采集 条件的出现。数据质量:
因为每个网站页面都不一样,尤其是同一个网站的详情页结构也不同,使得特征数据的提取比较困难,所以使用htmlunit+jsoup+regularity三种方法结合使用来采集特征数据。
采集效率:
因为采集中有很多网站,假设每次任务执行打开一个列表页和十个详情页,那么一千个任务执行一次需要采集11000个页面. ,所以url和详情页是分开的采集,通过mq实现异步操作,url和详情页的采集是通过多线程实现的。
被封锁的ip:
对于一个网站,假设每半小时执行一次,网站一天会被扫描48次,同样假设一个采集会打开11个页面,也是一天528次,所以被屏蔽是很常见的问题。解决方案,htmlunit提供了proxy ip的实现,使用proxy ip可以解决ip被阻塞的问题。代理ip的来源:一个是网上有很多网站卖代理ip的,你可以直接买代理ip,另一个是爬的,这些卖代理ip的网站提供一些免费的代理ip,可以把这些ip爬回来,然后用httpclient或者其他方法验证代理ip的可用性,如果可以的话,直接入库,建一个自己的代理ip库。因为代理ip是时间敏感的,
网站失败:
网站 失败有两种类型。一是网站的域名已经失效,无法直接打开原网址。第二个是网站的修改,原来配置的所有规则都失效了。无法采集接收有效数据。解决这个问题的方法是每天发送采集邮件提醒数据和日志,将那些没有采集到的数据和没有打开的页面汇总,通过邮件发送给相关人员。
验证码:
当时对于一个网站采集历史数据采集,方法也是通过他们的列表页到采集详情页,采集@ > 几十万条数据 后来发现这个网站 采集不到数据。看完页面,发现列表页面已经添加了一个验证码。这个验证码还是比较简单的,就是数字和字母。当时想在列表页加个验证码。? ,然后想了个办法,找了一个开源的orc文本识别项目tess4j(怎么用可以看这个),可以用,识别率20%左右,因为htmlunit可以模拟浏览器中的操作做,所以代码中的操作是先通过htmlunit的xpath获取验证码元素,
ajax加载数据:
一些网站使用ajax加载数据。使用htmlunit采集时,这种网站需要在获取到HtmlPage对象后,给页面一个加载ajax的时间。HtmlPage 获取 ajax 加载后的数据。
代码:webClient.waitForBackgroundJavaScript(time); 您可以查看稍后提供的演示
系统整体架构图,我们说的是这部分数据采集系统
演示
爬虫的实现:
@GetMapping("/getData")
public List article_(String url,String xpath){
WebClient webClient = WebClientUtils.getWebClientLoadJs();
List datas = new ArrayList();
try {
HtmlPage page = webClient.getPage(url);
if(page!=null){
List lists = page.getByXPath(xpath);
lists.stream().forEach(i->{
DomNode domNode = (DomNode)i;
datas.add(domNode.asText());
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
webClient.close();
}
return datas;
}
复制代码
上面的代码实现了采集一个列表页面
爬博客园
请求此网址::9001/getData?url=;xpath=//*[@id="post_list"]/div/div[2]/h3/a
网页:
采集 返回的数据:
再次爬上csdn
再次请求::9001/getData?url=;xpath=//*[@id="feedlist_id"]/li/div/div[1]/h2/a
网页:
采集 返回的数据:
采集步骤
经过一个方法去采集两个网站,经过不一样url和xpath规则去采集不一样的网站,这个demo展现的就是htmlunit采集数据的过程。
每一个采集任务都是执行相同的步骤
- 获取client -> 打开页面 -> 提取特征数据(或详情页连接) -> 关闭cline
不一样的地方就在于提取特征数据
复制代码
优化:使用模板法设计图案,提取功能部分
上面的代码可以提取为:一个采集执行器,一个自定义的采集数据实现
/**
* @Description: 执行者 man
* @author: chenmingyu
* @date: 2018/6/24 17:29
*/
public class Crawler {
private Gatherer gatherer;
public Object execute(String url,Long time){
// 获取 webClient对象
WebClient webClient = WebClientUtils.getWebClientLoadJs();
try {
HtmlPage page = webClient.getPage(url);
if(null != time){
webClient.waitForBackgroundJavaScript(time);
}
return gatherer.crawl(page);
}catch (Exception e){
e.printStackTrace();
}finally {
webClient.close();
}
return null;
}
public Crawler(Gatherer gatherer) {
this.gatherer = gatherer;
}
}
复制代码
给Crawler注入一个接口,这个接口只有一个方法 crawl(),不同的实现类实现这个接口,然后自定义特征数据的实现
/**
* @Description: 自定义实现
* @author: chenmingyu
* @date: 2018/6/24 17:36
*/
public interface Gatherer {
Object crawl(HtmlPage page) throws Exception;
}
复制代码
优化代码:
@GetMapping("/getData")
public List article_(String url,String xpath){
Gatherer gatherer = (page)->{
List datas = new ArrayList();
List lists = page.getByXPath(xpath);
lists.stream().forEach(i->{
DomNode domNode = (DomNode)i;
datas.add(domNode.asText());
});
return datas;
};
Crawler crawler = new Crawler(gatherer);
List datas = (List)crawler.execute(url,null);
return datas;
}
复制代码
不同的实现,只需要修改这部分接口实现即可。
数据
最后看一下使用 采集system采集 的数据。
影响
效果还是不错的,最重要的是系统运行稳定:
采集历史数据每天新增600万到700万之间采集数据增量在1万左右。系统目前配置了大约1200个任务(一个预定的实现会去采集这些网站)数据
系统配置采集的网站主要针对全国各省市县的招标投标网站的招标信息(目前1200多个采集 @> 站点已配置)。采集的数据主要作为公司logo的数据中心,为1个PC端网站和2个微信公众号提供数据
欢迎关注并掌握第一手资讯
以PC端显示的一个采集的中奖数据为例,看看采集的效果:
本文只是大致记录了采集系统从零到完整的过程,虽然还有很多问题本文没有提到。