首页未分类 › linux-网页抓取

linux-网页抓取

        最近一直在学习stevens的unix网络编程,对于网络通信有了一定的认识,所以也想练练手。聊天程序之前用winsock做过,这次不想做重复的。 之前看到一哥们写过windows下抓取猫扑的帖子,我觉得抓页面也是一个不错想法。我也喜欢逛猫扑,有时候也去追追里面写的文章,猫扑帖子少了一个很重 要的功能,就是只看楼主的帖子。猫扑水人很多,容易把楼主的帖子淹没在大海里面。

        查看了一下猫扑帖子的网页源代码,帖子内容介于<divclass=”box2 js-reply”data-rid=”*”& gt;和</div>之间,只需要解析这段内容,就能得到自己想要的东西。不过里面东西比较多,比较杂,还是先找一个简单页面抓取试试。 csdn博客相对来说就是个不错的选择,第一没广告,内容不算很多,第二,代码风格很好。抓CSDN的页面无非获得博主名,文章名字和URL等,如果想获 得更多的信息,可以把博主的排名,评论数抓取下来。

下面简单分析一下CSDN博客源代码。

博主标题:

文章标题和URL:

文章访问次数,评论次数等:

博客统计信息:

 从上面贴出的HTML可以看出,所需要的信息都在某一个id下,每个id是唯一的,这对解析是很有利的。我们只需要抓取到网页,分析相应内容,得到想要的信息即可。

在确定CSDN博客是可以抓取后,就可以着手抓取。如何抓取?简单来说,就是与CSDN博客服务器简历tcp连接,然后发送HTTP请求,得到响应。页面抓取过程如下图所示:

1337088036_1836

主要流程:

  1. 解析域名(csdn.blog.net),得到服务器IP地址
  2. 与服务器端建立TCP连接
  3. 发送HTTP请求
  4. 得到服务器端响应,响应内容里面含有请求页面源代码
  5. 解析网页源代码,得到所需要信息,如果需要抓取博主所有的文章,需要解析出每篇文章的URL
  6. 统计博主文章数,判断是否有分页,如果又分页,则请求分页内容,获取分页的文章URL
  7. 跳转到第一步,请求每篇文章
  8. 把文章保存到本地
  9. 根据需求看是否对文章进行处理  

知道流程后,就可以着手编码。先来看看我目前作出来的效果图。

1337091717_9097
这里并不只是把文章信息解析出来,也把每篇博客具体内容给存到本地了。存在以博主名命名的文件夹下,每篇文章存在以文章命名的html文件中。

1337091717_6343

具体实现:

一、解析域名

采用gethostbyname方法。函数声明如下:

执行成功,返回非空指针,失败返回空指针,并设置h_errno,可以通过hstrerror方法查看h_errno对应的错误提示信息。

函数中用到的hostent结构体,如下所示:

二、获得IP地址后,与CSDN博客服务器建立TCP连接。

解析域名和建立TCP链接,我都放在一个自定义函数buildconnect里面。每次需要建立连接,我只需要调用这个方法即可。代码如下:

不需要每一次都去解析域名,所以把域名存在一个static变量里面。

三、发送HTTP请求

HTTP请求格式如下所示:

说明:GET:表明是一个GET请求,还有POST请求(你可以模拟登陆,发送用户名和密码到服务端。不过现在CSDN登陆需要一个随机码验证。这个不好办)/lanyan822表示请求的页面,HTTP1.1表示使用的版本。\r\n表示结束。
Accept:表示浏览器接受的MIME类型
Accept-Language:表示浏览器接受的语言类型
User-Agent:指浏览器的名字。呵呵,因为是模拟浏览器发请求,所以这里是假的
Host:服务器的域名和端口
Connection:用来告诉服务器是否可以维持固定的HTTP连接。HTTP/1.1使用Keep-Alive为默认值,这样,当浏览器需要多个文件时(比如一个HTML文件和相关的图形文件),不需要每次都建立连接。这里我每次请求页面后,我都选择关闭。
这里需要注意的是:HTTP请求格式,千万不能在里面多写空格什么的。我之前一直请求页面失败就是因为里面多了空格。最后以\r\n结束。

四、接受服务端响应,并存储请求页面

HTTP响应包括响应头和所请求页面的源代码。

HTTP响应头如下所示:

响应头部也是以\r\n结束。所以可以通过\r\n\r\n来判断响应头部的结束位置。

实现源码:

五、解析网页源代码,得到所需要信息

我主要解析了博客的文章名,文章URL,访问次数,排名,积分,原创文章数,转载文章数,翻译文章数,评论数。
源代码解析是按照所需要的信息在源代码中出现的顺序依次解析,先出现文章名,接着是文章的评论,发表日期等信息,接着解析博主的积分,等级等,最后解析博主发表的文章数。
解析用的最多的是strstr函数。

函数功能:查找needle在haystack中第一次出现的地址,查找成功,返回第一次出现的地址,查找失败返回0.类似于c++ string的find_first_of函数。

信息解析出来,需要存储下来。主要是存在自定义的数据结构里面。每一页(最多50篇文章)存储在struct Articles结构体里面,文章信息则存入struct ArticleInfo里面。页面存储结构如下图所示:
1337151741_1920
自定义的结构体:

续:

五、解析网页源代码,得到所需要信息

解析html并不复杂,只是有点麻 烦而已。因为页面不可能全部读入内存在解析,只有分开读。分开读就必须要考虑需要查找的标签有可能只读到一部分这种情况。如读1024字节数据,我需要在 里面找<div class=”article_title”>,碰巧的是,1024字节数据最后几位是<div class.在这种情况下,我不能简单的说1024字节里没有我所需要的,那就丢掉这1024字节。这样导致的后果解析出来的文章有遗漏。
需要解析的标签如下:

我的解析流程图如下所示,有待优化。
1337154685_6939

1、从保存的首页中读1024字节到数组。
2、strcat到2048个字节的数组,多定义一个2048字节的数组,就是为了解决查找标签有可能只读到一部分的情况。
3、判断状态,自定义5个状态

4、根据状态查找相应的标签位置。    如果状态为TAG_RESOLV_RANK,则查找<ulid=”blog_rank”>和</ul>是否在数组中。只有两个标签同时在数组中,才解析标签所包含的有用信息。当<ulid=”blog_rank”>存在,而</ul>不存在时,则丢掉<ulid=”blog_rank”>之前的数据,在读1024字节数据后在解析。引用我自己写的一段代码来分析一下。

 

5、循环,直到所解析的内容解析完毕

、根据解析出来的文章URL,发送HTTP请求,保存返回的网页源代码

我是以文章名作为文件的名字,这里需要注意一个问题,当文件名出现/时,必须把文件名中的/替换为别的符号,我是替换为_符号。因为/在linux中属于路径的一部分。

至此,linux网页抓取工具就宣告完毕。

 

接下来的工作:

1.网页抓取是单线程的,接下来会采用多线程模式。
2.网页抓取没有抓取图片。js,css,所以在有网络的情况下才能呈现该有的样子。
3.代码很多地方需要优化,有的地方有点乱。
4.准备做个windows版本。不考虑做界面。
5.揉和window版本和linux版本,做个通用版。其实通用版还不是不同操作系统调用不同的函数而已。

 

 

发表评论