python获取网页正文:goose-extractor

做项目要用到提取网页的正文内容,于是在网上找找有什么好用的库。先测试一下,发现python就有现成的库,叫做goose-extractor。

事不宜迟,开始安装吧。

pip install goose-extractor

什么?出现问题了。

在安装依赖的lxml的时候,出现了以下的错误提示:

Microsoft\\Visual C++ for Python\\9.0\\VC\\Bin\\amd64\\cl.exe’ failedwith exitstatus 2

找不到好的解决方案,最后祭出大杀器。直接用编译好的。

先打开http://www.lfd.uci.edu/~gohlke/pythonlibs/

ctrl+F搜索lxml,然后下载它对应的whl文件,下载之后pip install xxx.whl即可安装!

安装完这个比较难缠的lxml之后,就方便了,重新pip install goose-extractor

等待安装成功。

使用非常简单,给出一个实例代码,抓取《https://www.lookfor404.com/简易python爬虫–爬取冷笑话/》的正文内容。

# -*- coding: utf-8 -*-
from goose import Goose
from goose.text import StopWordsChinese
url  = 'https://www.lookfor404.com/%e7%ae%80%e6%98%93python%e7%88%ac%e8%99%ab-%e7%88%ac%e5%8f%96%e5%86%b7%e7%ac%91%e8%af%9d/'
g = Goose({'stopwords_class': StopWordsChinese})
article = g.extract(url=url)
print article.cleaned_text

效果不错,截取一下控制台的输出:
goose-extractor提取网页正文结果

但是速度并不快,具体里面怎么实现的还没看,但是它要用到python的结巴分词,估计这个耗时久一点,另外还有一个问题,就是我<code>标签里面的内容,被过滤掉了,所以抓取技术文章,有点不合适,哈哈。

python爬虫–抓取ajax更新的内容

虽然很多情况下,直接对一个url发出请求,就能得到页面的源代码,但是我们还得考虑这么一种情况,就是有的网站为了用户体验,采用了ajax技术–即不刷新页面而改变页面内容,那么我们该怎么获取这些内容呢?

从本质出发,ajax技术也就是这么一个过程:利用js来发起一个post请求–然后接收返回的数据–js改变页面内容。在大多数情况下,我们都是点击一个按钮,然后页面就更新了。从原理上来看,点击某个按钮,就会发送一个post请求(当然还有滚动鼠标发送请求的),服务器再返回结果,再将结果直接更新到页面上。我们的思路是–用爬虫发送同样的post请求,从而获得返回来的数据。

简单点说,对于爬虫而言,在获取数据上,get和post并无太多异样,只是get的话,我们只需要对某个链接发送请求就行,而post,则需要一些表单内容。那么,我们怎么知道该post什么内容过去,又能获得什么响应呢?

以虎嗅网(http://www.huxiu.com/)为例,我用python写了一个爬取它利用ajax更新的数据。

我们打开它的首页,拉到最下面,发现有个“点击加载更多”的按钮,颜色很浅。

点击它,就会有新的内容出现。

虎嗅网底部

我们要做的,就是看看点击这个按钮会发生什么。

我这里以遨游浏览器为例,大多数浏览器(比如chrome,Firefox)应该都有类似的功能,在网页上,点击右键–审查元素,在弹出的窗口菜单栏点击“网络”一栏,在这里,我们可以监控一些get,post等行为。

刚开始是空的。

此时,回到网页上去,点击“点击加载更多”按钮。然后再回到开发者工具那里,会发现,出现了很多的请求:

点击加载更多

其中,有个请求的方法是POST,这就是我们要找的。点进去,可以看到http报头、预览、响应和cookies。

从中,我们可以获得一些头部信息,以及post的表单数据。

  • 头部信息

不同网站对头部信息的要求不同,有的不需要头部信息都能响应,有的则需要头部信息的一部分,虎嗅是不需要头部信息的。

  • 表单数据

从开发者工具里面看,虎嗅需要的表单数据有两个,一个是’huxiu_hash_code‘,这个是用来防止csrf的,一个是page,代表页数,我们把huxiu_hash_code复制下来。

构造一个请求过去,会得到一个json格式的响应,内容有msg,total_page,result和data,要的文章数据在data里面。我的程序把data里面的标题都提取出来,打印到屏幕下,下面贴上代码:

#-*- coding:utf-8 -*-
__author__ = '李鹏飞'
import urllib2
import urllib
import json
import re
url = 'http://www.huxiu.com/v2_action/article_list'
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
data = {'huxiu_hash_code':'322cf76eec54c4e275d7c8122028a3b2','page':2}
data = urllib.urlencode(data)
try:
    request = urllib2.Request(url=url,data=data)
    response = urllib2.urlopen(request)
    #解析json
    result = json.loads(response.read())
    print result['msg']
    content = result['data']
    #匹配文章标题
    pattern = re.compile('
<div class="mob-ctt">.*?target="_blank">(.*?)</a>',re.S)
    #虎嗅文章标题
    items = re.findall(pattern,content)
    for item in items:
        print item
except urllib2.URLError, e:
    if hasattr(e,"code"):
        print e.code
    if hasattr(e,"reason"):
        print e.reason

这样子的话,大部分网页就能抓了,管他是post还是get。

简易python爬虫–爬取冷笑话

今天闲来无事,就想接触一下爬虫的基本知识,搞大数据,没有数据是万万不可的,而数据的来源,有很大一部分是通过网络爬虫来抓取的。

关于爬虫,似乎python的框架会比较多,比如scrapy。不过刚开始接触,用python一些原生的库就可以完成一些简单的小爬虫程序了,就比如我刚刚写的冷笑话爬虫。

在这之前,得大概搞清楚爬虫的工作原理,其实也很简单。首先是对你的目标网站发出http请求,目标网站会返回一个html文档给你,也就是我们俗称的网页,原本,这些文档都是由一大堆标签构成的,但浏览器配合上css,js,就形成了我们看到的各种各样的网页。但我们的爬虫不需要这些外衣,我们只需要内容,所以爬虫接下来的工作是处理这个html文档,提取我们要的信息,然后继续爬取下一个网页,如此往复循环。

不多说,先上我写的一段python脚本,爬取<我们爱讲冷笑话>的随机页面,网址为:http://lengxiaohua.com/random,这个页面每次刷新都有20则不同的随机冷笑话,我的程序功能是:输入数字1到20看对应的笑话,输入new重新爬取另外20则新笑话,输入quit退出程序。代码如下:

#-*- coding:utf-8 -*-
__author__ = '李鹏飞'
import urllib2
import re

class randomJoke:

    #初始化方法
    def __init__(self):
        self.url = 'http://lengxiaohua.com/random'
        self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
        #初始化headers
        self.headers = { 'User-Agent' : self.user_agent }
        #笑话内容
        self.content = []

    #获取网页源代码
    def getSourceCode(self):
        try:
            request = urllib2.Request(url = self.url, headers=self.headers)
            response = urllib2.urlopen(request)
            sourceCode = response.read().decode('utf-8')
            return sourceCode
        except urllib2.URLError, e:
            if hasattr(e,"reason"):
                print u"网络错误...",e.reason
                return None

    #获取笑话
    def setContent(self):
        sourceCode = self.getSourceCode()
        if not sourceCode:
            print('获取网页内容失败~!')
            quit()
        pattern = re.compile(' <pre.*?js="joke_summary".*?"first_char">(.*?)</span>(.*?)</pre>

.*?class="user_info">.*?<a.*?>(.*?)</a>.*?

(.*?)

',re.S)
        items = re.findall(pattern,sourceCode)
        self.content = items
        print u"已经爬取源代码...正在解析源代码..."

    #返回笑话
    def getContent(self):
        return self.content

    #打印一则笑话
    def printAJoke(self,number):
        joke = self.content[number]
        print u"作者:%s" %(joke[2])
        print u'发表于:'+ joke[3]
        #item[0]和item[1]组成完整的内容
        print joke[0]+joke[1]

randomJoke = randomJoke()
notQuit = True
print u"你好,这里是随机笑话!"
print u"---------------------"
randomJoke.setContent()
print u"...."
print u"笑话池已经装满,20/20"
print u"输入1到20看笑话~~,输入quit退出,输入new重新爬取新笑话"
while notQuit:
    input = raw_input()
    if input == "quit" :
        print u"bye!"
        notQuit = False
    elif input == "new":
        randomJoke.setContent()  #重新抓取笑话内容
        print u"...."
        print u"笑话池已经装满,20/20"
        print u"输入1到20看笑话~~,输入quit退出,输入new重新爬取新笑话"
    else:
        input = int(input)
        randomJoke.printAJoke(input-1)
        print u"--------------------------------------------------"
        print u"输入1到20看笑话~~,输入quit退出,输入new重新爬取新笑话"
print u"您已经成功退出!"
quit()

保存这段脚本,运行,效果如下:

运行冷笑话爬虫

说明一下,有些笑话是带图片的,而我没有抓取图片,仅仅抓取了作者,发表日期,和内容。

下面对程序稍微进行一下解释:

引入了urllib2re两个模块,urllib2是用来发送http请求的,re是正则表达式的相关模块。

randomJoke是程序的主要部分,下面就所有的方法,进行简单的说明。

__init__方法

位置:9-15行

这个是初始化方法,定义了四个变量,url为目标网址;user_agent是我们发送http请求的时候伪造的头部的一个客户端信息;headers是头部信息,这里只把user_agent信息加进去就行了;content[]是一个列表,用来存放笑话内容。

getSourceCode方法

位置:18-27行

这个方法是用来获取网页的源代码的。在第20行,发起一个http请求,接着读取返回的源代码,并return源代码。

setContent方法

位置:30-44行

这个方法是比较关键的一个方法,主要是利用正则表达式匹配源代码,并把我们需要的内容爬取下来。第35-41行代码就是正则表达式的匹配模式,我们要抓取网页中的20个笑话,其中的.*?是匹配任意字符,多一个括号,即(.*?)表示匹配的内容是我们需要的内容,在这个片段当中,出现了4(.*?),因此,程序的第36行,item表示的是一个列表,这个列表的形式是这样的:[[[item1-1],[item1-2],[item1-3],[item1-4]],[…],[…],…[[item20-1],[item20-2],[item20-3],[item20-4]]]。即这个列表有20个元素(代表这个网页的20个笑话),每个元素又是一个列表–列表中有4个元素(代表爬取的4个内容,对应前面说的4个(.*?)。这里附上其中的一小部分网页源代码,对应着网页的html源代码,我们就能感受到这个简单的正则表达式的作用了:


<pre js="joke_summary"><span class="first_char">来</span>表哥家吃饭,小侄子一边看西游记一边手舞足蹈上蹦下跳学猴哥。
我哥:“傻儿子,你是孙悟空吗?”
侄子:“正是,你孙爷爷在此。”
打到现在还在哭。</pre>
</div>
<div class="para_info clearfix">
<div class="user_info">
                        <img src="http://joke-image.b0.upaiyun.com/img/user/cover/630347-299927_40x40.jpg" class="left corner4px" height="40" width="40"/>
                        <a href="/user/630347">离愁▍ Feast aw</a>
                        <p>15天前</p>

一共有20个类似的html片段,正则表达式的作用就是把笑话内容,作者和发表日期提取出来。

printAJoke方法

位置:51-56行

打印一则笑话,在这里我们更能感受到content[]的结构了,因为printAJoke这个方法需要传一个number参数进来,范围是1到20,我们就可以读取对应的笑话了,比如content[0]代表第一则笑话,和java中的数组类似。

53行以后就是程序运行的基本逻辑了,比较简单,就不多解释了。

就这样,一个简单的爬虫就完成了!