소개
주말에 할 일도 없고 심심해서 php를 이용해서 블로그 크롤링 시스템을 만들었습니다. 물론 cnblogs도 자주 방문합니다. 내 크롤링 비교 간단합니다. 웹 페이지의 콘텐츠를 얻은 다음 정규 매칭을 사용하여 원하는 것을 얻은 다음 데이터베이스를 저장합니다. 물론 실제 프로세스에서는 몇 가지 문제가 발생합니다. 이 작업을 하기 전에 이미 생각해 봤는데, 나중에 csdn, 51cto, Sina 블로그 등의 콘텐츠를 추가하고 싶다면 쉽게 확장할 수 있으면 좋겠습니다.
저런 것들은 잡을 수 있나요?
먼저 말씀드리고 싶은 점은 이것이 단순한 크롤링이라는 점입니다. 웹페이지에 표시되는 모든 항목을 크롤링할 수는 없습니다.
그 중 읽은 횟수, 댓글 수, 추천 수, 반대 수, 댓글 수... 이것들은 ajax를 호출하는 js를 통해 동적으로 얻어지기 때문에 실제로 한 문장으로 웹 페이지를 열면 얻을 수 없습니다. 소스코드를 보려면 마우스 오른쪽 버튼을 클릭하세요. 이 간단한 크롤링에는 문제가 있을 수 있습니다. 전에 기사를 봤는데, 누군가 먼저 브라우저를 통해 웹페이지를 로드한 다음 전체 DOM을 필터링합니다(기사에서 언급했듯이 이는 매우 비효율적입니다). 물론 이러한 js 요청을 연결하는 것도 가능하지만 그렇게 될 것입니다. 아마 더 귀찮을 것 같아요.
크롤링 아이디어
먼저 크롤링 깊이에 대해 이야기해 보겠습니다.
예를 들어 링크 a에서 크롤링을 시작합니다. 깊이가 1이면 현재 링크의 내용을 가져옵니다. 깊이가 2인 경우 링크 내용에서 지정된 규칙에 따라 링크를 일치시킵니다. a. 일치하는 링크도 깊이 1로 처리됩니다. 깊이는 링크의 깊이와 수준입니다. 이 방법으로만 크롤러가 "크롤링"할 수 있습니다.
물론, 링크를 이용해 특정 콘텐츠를 크롤링할 경우 크롤링할 수 있는 항목이 매우 제한되거나, 일어나기도 전에 사망할 수도 있으므로(이후 레벨이 콘텐츠와 일치하지 않음) 크롤링할 때 설정하면 됩니다. 가져올 때 여러 시작 링크. 물론, 크롤링 시 중복된 링크가 많이 발생할 가능성이 있으므로 크롤링된 링크를 표시하여 동일한 콘텐츠가 반복적으로 획득되어 중복이 발생하는 것을 방지해야 합니다. 이 정보를 캐시하는 데는 여러 변수가 있으며 형식은 다음과 같습니다
먼저 해시 배열이고, 키 값은 URL의 md5 값, 상태는 0, 다음과 같은 형태로 고유한 URL 배열을 유지합니다.
<span>Array</span><span> ( [bc790cda87745fa78a2ebeffd8b48145] </span>=> 0<span> [9868e03f81179419d5b74b5ee709cdc2] </span>=> 0<span> [4a9506d20915a511a561be80986544be] </span>=> 0<span> [818bcdd76aaa0d41ca88491812559585] </span>=> 0<span> [9433c3f38fca129e46372282f1569757] </span>=> 0<span> [f005698a0706284d4308f7b9cf2a9d35] </span>=> 0<span> [e463afcf13948f0a36bf68b30d2e9091] </span>=> 0<span> [23ce4775bd2ce9c75379890e84fadd8e] </span>=> 0 ......<span> )</span>
두 번째는 얻을 수 있는 URL 배열입니다. 이 장소도 최적화할 수 있습니다. 배열에 대한 모든 링크를 얻은 다음 배열을 반복하여 콘텐츠를 얻습니다. 모든 콘텐츠에서 1을 뺀 최대 깊이를 두 번 얻었습니다. 여기에서는 다음 레벨 콘텐츠를 얻을 때 콘텐츠를 직접 얻을 수 있으며, 그런 다음 위 배열의 상태를 1(이미 획득함)로 변경하면 효율성을 높일 수 있습니다. 먼저 링크를 저장하는 배열의 내용을 살펴보세요.
<span>Array</span><span> ( [</span>0] => <span>Array</span><span> ( [</span>0] => http:<span>//</span><span>zzk.cnblogs.com/s?t=b&w=php&p=1</span> <span> ) [</span>1] => <span>Array</span><span> ( [</span>0] => http:<span>//</span><span>www.cnblogs.com/baochuan/archive/2012/03/12/2391135.html</span> [1] => http:<span>//</span><span>www.cnblogs.com/ohmygirl/p/internal-variable-1.html</span> [2] => http:<span>//</span><span>www.cnblogs.com/zuoxiaolong/p/java1.html</span> ......<span> ) [</span>2] => <span>Array</span><span> ( [</span>0] => http:<span>//</span><span>www.cnblogs.com/ohmygirl/category/623392.html</span> [1] => http:<span>//</span><span>www.cnblogs.com/ohmygirl/category/619019.html</span> [2] => http:<span>//</span><span>www.cnblogs.com/ohmygirl/category/619020.html</span> ......<span> ) )</span>마지막으로 모든 링크가 배열로 결합되어 반환되고 프로그램은 루프를 통해 연결의 콘텐츠를 가져옵니다. 위 획득 레벨이 2인 것과 마찬가지로 레벨 0의 링크 내용을 획득하였으며, 레벨 1의 링크 획득에만 사용됩니다. 레벨 1의 링크 내용도 모두 획득되었으며, 획득에만 사용됩니다. 링크를 레벨 2에 저장합니다. , 콘텐츠가 실제로 획득되면 위 콘텐츠를 다시 획득하게 되며 위 해시 배열의 상태는 사용되지 않습니다. . . (최적화 예정).
기사를 얻는 데에도 규칙적인 규칙이 있습니다. 블로그파크의 기사 내용을 분석해 보면 기사의 제목과 본문은 기본적으로 매우 정기적으로 얻을 수 있는 것으로 나타났습니다
제목과 제목 html 코드는 아래와 같은 형식으로 되어 있으며, 다음 정규식을 사용하면
을 쉽게 일치시킬 수 있습니다.
<span>#</span><span><a\s*?id=\"cb_post_title_url\"[^>]*?>(.*?)<\/a>#is</span>
텍스트, 텍스트 부분은 정규식의 고급 기능인 밸런스 그룹을 통해 쉽게 구할 수 있지만, 오랫동안 작업한 결과 PHP에서는 밸런스 그룹을 잘 지원하지 않는 것 같습니다. , 그래서 잔액군을 포기하고 추가했습니다. html 소스코드를 보면, 다음의 규칙적인 규칙을 통해 기사본문의 내용을 쉽게 일치시킬 수 있음을 알 수 있습니다. 각 기사는 기본적으로 아래 그림과 같은 내용을 가지고 있습니다
<span>#</span><span>(<div\s*?id=\"cnblogs_post_body\"[^>]*?>.*)<div\s*id=\"blog_post_info_block\">#is</span>
시작:
끝:
博客的发布时间也是可以获取到的,但有些文章在获取发布时间的时候可能会找不到,这个就不列在这里了,有了这些东西就可以爬取内容了。
开始爬取
开始爬取内容了,最初我设置的爬取深度是2级,初始页面是博客园首页,发现爬取不了多少内容,后来发现博客园首页有个页码导航
就试图拼接成页码格式http://www.cnblogs.com/#p2,循环200次,以每页为起始页面,深度为2去抓取。但我高兴的太早了,开了几个进程跑了好久程序,抓了几十万条,后来发现完全在重复,都是从第一页中抓取的,因为博客园首页点击导航的时候(除了第一页),都是ajax请求获取到的。。。。看来博客园还是考虑到这个问题,因为大多数人都是只打开首页,不会去点击后面的内容(我可能偶尔会去点击下一页),所以为了在防止初级抓取者去抓取和性能发面做权衡,将第一页设置为静态网页的方式,缓存有效期是几分钟(或者是根据跟新频率,当更新多少篇的时候去更新缓存,或者两者的结合),这也是为什么有时候发布的文章,过一会儿才会显示出来的原因(我猜的^_^)。
难道不能一次性抓取很多内容吗?后来我发现这个地方使用的全部是静态网页
从找找看这个地方获取到的内容都是静态的,包括最下面的导航链接中的所有页面都是静态的,而且,这个搜索右边还有筛选条件,可以更好的提高抓取的质量。好了有了这个入口,就可以获取到好多高质量的文章了,下面是循环抓取100页的代码
<span>for</span>(<span>$i</span>=1;<span>$i</span><=100;<span>$i</span>++<span>){ </span><span>echo</span> "PAGE{<span>$i</span>}*************************[begin]***************************\r"<span>; </span><span>$spidercnblogs</span> = <span>new</span> C\Spidercnblogs("http://zzk.cnblogs.com/s?t=b&w=php&p={$i}"<span>); </span><span>$urls</span> = <span>$spidercnblogs</span>-><span>spiderUrls(); </span><span>die</span><span>(); </span><span>foreach</span> (<span>$urls</span> <span>as</span> <span>$key</span> => <span>$value</span><span>) { </span><span>$cnblogs</span>->grap(<span>$value</span><span>); </span><span>$cnblogs</span>-><span>save(); } }</span>
至此,就可以去抓去自己喜欢的东西了,抓取速度不是很快,我在一台普通pc上面开了10个进程,抓了好几个小时,才获取到了40多万条数据,好了看看抓取到的内容稍微优化之后的显示效果,这里面加上了博客园的基础css代码,可以看出效果和
抓取到的内容稍作修改:
原始内容
再看下文件目录结构,也是用上篇的自制目录生成工具生成的:
+myBlogs-master
+controller
|_Blog.php
|_Blogcnblogs.php
|_Spider.php
|_Spidercnblogs.php
+core
|_Autoload.php
+interface
|_Blog.php
+lib
|_Mysql.php
+model
|_Blog.php
|_App.php
效果还是很不错的,这里再猜下推酷这种专门爬取的网站的工作方式,一个常驻进程,隔一段时间去获取一次内容(比如说首页),如果有新鲜的内容入库,没有的话放弃这次获取的内容,等待下次获取,当时间很小的时候就可以一篇不漏的抓取的”新鲜“的内容。
这是github地址:
github——myBlogs
本文版权归作者iforever(luluyrt@163.com)所有,未经作者本人同意禁止任何形式的转载,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
以上就介绍了博客爬取系统,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。