网络爬虫编写实践漫谈(一个集抓取与发布的爬虫程序的思路)
在公司里,有两个人从事文字方面的工作,一个是文案,一位是行政人员,兼职做下编辑。可谓是编辑奇缺,一天只能写10篇左右的文章,效率不可谓之不低。究其原因,很多时间都花费在寻找资料上面了。
人虽少,事情还是得做,10多个网站等着编辑更新,按照目前的编辑文章发布速度,一天更新一个网站,得10天才能轮上一回,根本无法满足搜索引擎的胃口。为了把时间赢回来,我开始编写一个网络爬虫,顺便说一句,我现在在医疗行业,没有技术人员,勉为其难,咱得搞定它。
这个爬虫的设计和开发工作,全由我一人完成。它的目标是采集健康资讯类文章、药品、百科、及问答,种子网站是39健康网、99健康网等近10个大型健康门户,工作量确实挺大的,毕竟这么大的爬虫体系还是第一次搞,之前虽也用过python写过一些小型爬虫,但那太小了,只在一个网站里,而现在要做的工作,超过10倍都不止,这次用的是java和python混合开发,小伙伴们很关心,这个爬虫会开源吗?只能很抱歉,这项工作是在工作时间内做的,暂不提供开源版本,但会将遇到的问题分享一下,如有任何不足之处,也欢迎您的批评。
我的配置:
硬件部分:
勿需怀疑:爬虫这种完全依赖网络环境的程序,网速是真的是非常重要,于是建议,拉根像样的线(也就20MB电信),电脑配置嘛,稍微好些,I5,I7也差不多了,小公司单配服务器不现实,虽然服务器会更好,然而,从成本上来讲,你懂的,按下不说。
软件部分:
java1.8,虽说1.8不如1.7普及,还是向前看吧,甲骨文公司能把JAVA更新到1.8,肯定能说明比1.7要好那么一点。操作系统用的是:linux Mint,凭心而论,不喜欢ubuntu而喜欢debian,可能是由于折腾得太厉害吧,ubuntu崩溃几率太高,debian则坚如磐石,无奈的是博主用的是dell灵越系列的笔记本,无线驱动在debian怎么都搞不定,而ubuntu却可以。怎么办?最终选择了Linux Mint,是带有无线驱动的,只能妥协了,庆幸的是,它基于ubuntu却比ubuntu要稳定许多,用了一段时间,也没报错,就选它了。IDE的选择,折腾过netbeans、idea和eclipse,最终还是选择了eclipse,免费的还不错,虽有人说没有idea好,但idea是工花银子的,而且价格不菲,虽说网上也大牛分享了注册码,也有破解版,但作为有可能成为开发者的人来讲,版权意识还是得有的嘛。
第一关:反爬策略
爬虫是小偷,游走在别人的网站里,它不是搜索引擎,不是百度爬虫,它没有特权,网站站长想着怎么样封杀它、把内容藏起来,让爬虫找不到,而永远不会像对待百度spider那样,想尽一切办法把内容呈到它面前。也不能怪站长太现实,实际上,私有爬虫程序也的确是劣迹斑斑:它把被爬站的内容无偿拿走、增加服务器负担、更严重的爬虫会占据过多带宽,从而使被爬网站瘫痪,或者影响网站的正常运行。于是乎,站长们、服务器管理员会制定各种反爬虫策略:
-
检查访问者header信息,是爬虫直接拒绝它的访问,别说抓取内容了,直接拒之门外;
-
限制单IP访客访问页面的频率,每分钟阅读超过10个页面的直接报错处理等等。
-
来源检查,不符合规范的直接pass掉;
-
……
总之,第一关就很不好过了,更别说那些需要登陆查看内容的、回复见内容的、内容用ajax加载的了。新技术的应用,使原本单纯的html页面变得愈来愈复杂了,可怜了爬虫开发人员,爬个站,真TMD不容易。第一关解题的关键是我得学会伪装,得像个浏览器,像个真实的访客,速度保持恰当的频率,“腰牌”上得甩掉爬虫标签,得像个真实的人,OK,过关了。进入网站后,就像到了仓库,各种“宝贝”享用不尽,别忙,进去了,还得解析网页,才能拿到想要的。
第二关:网站内容解析
破了第一关,原本还沾沾自喜,好景不长,第二关等着我呢,满屏的html,script,style交叉排列,文字信息全躲在里面了。要拿到可真不容易啊。好在有htmlparser、htmlunit、jsoup之流,有这三件宝,可谓是利剑在手,一切我说了算。使用jsoup写个获取本站首页标题的例子。例1:
/* * 作者:fedkey * 网站: yangshengliang.com */ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import org.jsoup.nodes.Element; import org.jsoup.nodes.Document; import org.jsoup.Jsoup; public class GetBlogTitle { public static void main(String[] args) throws MalformedURLException { // 版本1 String title = GetBlogTitle.getBlogTitle(); System.out.println("版本1:"+title); //版本2 String title2 = GetBlogTitle.getTitle(); System.out.println("版本2:"+title2); } @SuppressWarnings("finally") public static String getTitle() { String url = "https://www.yangshengliang.com"; String title = null; try { Document doc = Jsoup.connect(url).get(); Element tmp = doc.getElementsByTag("title").get(0); title = tmp.text(); } catch (IOException e) { e.printStackTrace(); } finally { return title; } } public static String getBlogTitle() throws MalformedURLException { URL url = new URL("https://www.yangshengliang.com"); String title = null; //标题 try { StringBuffer buffer = new StringBuffer(); //放html结果 HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.connect(); BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line = null; while ((line = reader.readLine()) != null) { buffer.append(line); } Document doc = Jsoup.parse(buffer.toString()); // 创建可操作的jsoup Docuement对象 Element tmp = doc.getElementsByTag("title").get(0); //获取title title = tmp.text(); } catch (IOException e) { e.printStackTrace(); } return title; } }
都输出了正确的结果:
版本1:湖南SEO_杨圣亮博客_专注:网站优化/建设_网络营销工具开发 版本2:湖南SEO_杨圣亮博客_专注:网站优化/建设_网络营销工具开发
上面给出了两个方法,版本2 getTitle()方法明显比版本1getBlogTitle()简洁不少。想想,如果博客的服务器应用了反爬虫策略,要检查请求头,那么上面的代码肯定就不能达到预想的结果了,真实的情况比上面要复杂很多。好好啃htmlunit和jsoup就能帮助我们解决问题,对于那些ajax加载的页面,要获取它的内容,如果htmlunit不行,就只能 selenium了。
例2:获取一篇文章的内容。
还是拿博客开刀,url为:https://www.yangshengliang.com/seo-ji-shu/seo-jiaocheng/197.html
/* * 作者: fedkey * 网站: yangshengliang.com * url: https://www.yangshengliang.com/seo-ji-shu/seo-jiaocheng/197.html * 时间:2016-12-9 */ import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; public class GetContentAndTitle { private static Document html = null; // 存储html源代码 public static void main(String[] args) { String url = "https://www.yangshengliang.com/seo-ji-shu/seo-jiaocheng/197.html"; GetContentAndTitle.getPageSource(url); String title = getTitle(); System.out.println("title:"+title); String content = getContent(); System.out.println("正文:"+content); } // 先来获取html源代码 private static void getPageSource(String url) { try { html = Jsoup.connect(url).get(); } catch (IOException e) { e.printStackTrace(); } } // 获取title private static String getTitle() { // return html.title(); //返回title,过于简单,不使用,用getElementsByTag()方法获取。 Element titleElement = html.getElementsByTag("title").get(0); String title = titleElement.text(); return title; } // 获取正文 private static String getContent() { // 通过分析网页html源码,得出的结果是div = "view-content" 的标签包裹的就是文章内容 Element contentHtml = GetContentAndTitle.html.getElementsByClass("view-content").get(0); //只有一个class为view-content的标签,因此是第一个 String content = contentHtml.text(); // 取到文字内容 return content; } }
运行代码,得到结果:
title:百度如何识别手机站和pc 站,加什么代码(已解决)_杨圣亮的个人网站 正文:问题如标题: 百度如何识别手机站和 PC 站?加什么代码啊?进入移动互联网之后,手机端流量超过电脑端,面对流量暴增的移动端,手机网站的SEO优化就不可忽视了,第一个问题来了。如何让百度(国内主要是百度,相信其他搜索引擎也会跟进)有效识别哪个是PC端,哪个是手机站? 其实搜索引擎是可以通过一定的代码检测方法来识别PC端与wap的问题,不过,为了避免出现失误。手动设置一下,还是很有必要的。 网站类型分三大类 1.PC站 2.手机站 3.响应式自适应网站 帮助搜索引擎(百度)识别 方法:在页面的<head>与</head>之间添加代码以下代码 PC站:<meta name=”applicable-device” content=”pc”> 手机站:<meta name=”applicable-device” content=”mobile” /> 响应式网页可标识:<meta name=”applicable-device”content=”pc,mobile”> 这样,百度就可以按照站长的设置,来区别对待PC或手机站了! 扩展知识: 在PC站网页里指定对应的手机站url,有些时候,PC站和手机站外观独立、域名不一样、共享数据库的情况下,PC站与手机站的URL几乎一样,只有微小的区别。比如:PC站URL为: www.abc.com/z/12.html 对应的手机站url为: m.abc.com/z/12.html 这种情况下,如果想要让搜索引擎主动将PC与手机站之间的页面一一对应起来。虽然在百度站长平台,已经有了【移动适配】,可以对应URL进行提交。 通过正则表达式可以达到全站适配的效果,不过,一像很懒的我,更愿意在网页的html中进行指定,这样的好处在于搜索引擎爬虫在处理PC端页面的时候,能够第一时间就和手机网页对应起来。同样是在html页面中的 head标签中插入代码,这次也是meta标签,这已经是除了关键字(keywords)与描述(description)之外的关于meta标签元素的用途了,可见:meta 标签对网站建设和seo的重要性可见一斑。当然,meta的用法还有很多,以后会写专门的文章来介绍,这里只说如果通过meta标签来指定与该页面对应的移动端url。 例: <meta name="mobile-agent" content="format=xhtml; url=http://m.bb.com/abc.html"> <meta name="mobile-agent" content="format=html5; url=http://m.bb.com/abc.html"> 例中的 url后面写移动端的url地址即可。
结果是正确 的,然而,得到纯文本其实并不是最好的,拿到数据后,通常是需要发布,没有了html格式,文字全堆在一起,是很难看的。常用的做法是选择性地去掉一些标签,如:script、style、a、div、<!–*–>等,如此一来,拿到的结果再次发布或使用浏览器查看,格式基本没变,是多么舒服的一件事,可见,让文字保持基本排版格式是多么重要啊。
第四关:数据存储
数据抓取到后,就需要存储起来,或写到硬盘里,或写入数据库中。数据存储要考虑效率和性能问题,毕竟多任务执行起来,多线程操作对数据存储也是个挑战,个人倾向于将文件直接写入txt中,然后编写发布模块,直接发布到相应地网站里。毕竟不是做API,没必要存储太多的数据。
第五关:URL列表维护
三个URL列表:已抓取URL列表、未抓取URL列表、失效URL列表。考虑到数据比较大,运算起来耗内存,全部读入内存不现实,很快就得报错。我在mysql数据库里分别建了三张表,分别存储相应的列表,运算起来性能也还不错。随着抓取的页面增加,这三个列表,肯定会越来越大,既要保证效率,又要准确,网友推荐了 布隆过滤器(Bloom Filter),确实是个了不起的算法实现。
第六关:内容去重
这是一项艰巨的任务,涉及到机器学习了,搜索引擎也一直在做这个任务(检查重复页面,是否原创),暂时没有过多的研究,只是在URL列表中,不抓取重复的URL,以达到最基本的去重复。
第七关:数据发布
数据发布应该是最简单的了,我是用来发布到网站的,通过编写代码模拟一下平时发布文章的过程,就能实现了。当然,也可以分析一下发布网站的程序,将数据直接推到数据中,也是可以了,灵活运用吧,偷懒,这一步,直接用python写了。
- java htmlparser 获取网页title
- drupal 7 移除自带的 css 和 javascript 及 jQuery.extend(Drupal.settings,
- 宇秀下拉 2021-2-16下拉更新案例
- java实现百度站长平台网页链接批量提交(附源代码)
- 学习seo必须会编程吗?做seo会编程的意义
- pacman 安装软件时不能自动补全(已解决)
- 百度UEditor-KityFormula for wordpress 2.0.1发布
- [免费]百度竞价创意批量采集器1.0免费下载
- 宇秀搜索引擎下拉与相关搜索营销系统格式定制重要说明 从6.473版本开始
- 网站编辑如何写受搜索引擎喜欢的seo原创文章
三五营销
2016年12月15日 下午3:40
挺好的,祝你快乐