Python爬虫指归(一)


最近面临一个采集语料的任务,要求以百度搜索为入口,从搜索结果中检索到需要的语料。这样一来,面临的情况就相当复杂,于是好好研究了一下自动化测试工具Selenium,用这玩意儿做爬虫简直是万能的。可以把它当成一个浏览器的控制工具,也就是说,只要浏览器可以访问的页面,都可以爬下来。Selenium的使用不算复杂,但是使用的过程中发现,这里面的坑真不是一般的多!这篇文章其实更适合叫做 “Python爬虫踩坑记” ,因为每条经验都是踩坑踩!出!来!的!!!啊……

一、前期准备

1、安装Selenium

1
pip install selenium

2、安装lxml

lxml是一个用来解析html文件的包,可以很方便地通过xpath语法定位html中的标签和元素。安装也很简单,pip一下就好了。

1
pip install lxml

3、安装浏览器及driver

推荐Selenium搭配Chrome使用。

在我的系统(OpenSuse Leap 42.2)上,使用Selenium(version:3.4.3)搭配PhantomJS和Firefox都会出现一个奇怪的问题,就是在访问某些页面的时候会卡住,既不报错也不返回结果,尝试使用 implicitly_wait()set_page_load_timeout()set_script_timeout() 几个函数设置超时都没效果,而且更换User-Agent之后也没有用,换用Chrome之后问题消失。

Windows用户在安装Chrome时,需要注意只能安装 32位版本 (因为稍后需要的chromedriver只支持32位版),并且 不要 更改Chrome默认的安装路径,不然将无法使用。之后,需要下载一个chromedriver ,最新版是2.31,原下载地址被墙,好在我们有镜像:

http://npm.taobao.org/mirrors/chromedriver/2.31/

下载好之后,

Windows用户 :在C盘新建一个chromedriver文件夹,并把chromedriver.exe这个文件放进去。然后在环境变量(PATH)中添加“C:\chromedriver\”。

Linux用户 :把chromedriver文件放入/usr/bin/路径中。

4、测试

在Python交互环境中输入如下代码:

1
2
from selenium import webdriver
driver = webdriver.Chrome()

这时,桌面上应该会打开一个空白的Chrome浏览器窗口。继续输入:

1
driver.get("http://www.baidu.com")

就打开了百度的主页面。

如果出现异常,请检查如上步骤是否做好。或者也可以在下方留言,反正看我博客的人也不多……

二、网页及xpath简介

1、关于网页

爬虫的目的是为了在网页上爬取有用的信息,这也就要求我们要对网页的组成最起码有基础的了解。基本上每种浏览器都提供了查看网页源代码的方式,在一张网页上点击右键,一般就可以找到类似的选项。这时,可以看到具有如下基本结构的代码:

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<head>
<h1> Title </h1>
... ...
</head>

<body>
<p>
</p>
... ...
</body>
</html>

这是HTML(超文本标记语言)的基本结构,一个 <…> 叫做一个标签,标签一般是成对出现的,比如 <html></html>,带有斜杠的这种叫做闭合标签。每种标签具有不同的意义,比如:

  • <html> 与 </html> 之间的文本描述网页
  • <body> 与 </body> 之间的文本是可见的页面内容
  • <h1> 与 </h1> 之间的文本被显示为标题
  • <p> 与 </p> 之间的文本被显示为段落

浏览器就是根据这样的成对的标签,来加载每一个网页的。

标签一般会带有属性,比如:

1
<title lang="en">Harry Potter</title>

title标签就带有一个“lang”属性,且属性的值为“en”。

我们的目的是为了爬取信息,以上内容理解就可以,标签的意义并不需要识记。

2、关于xpath

我们通过爬虫爬取下来的网页,也具有如上的基本结构。那么,如何通过这些HTML标签,快速地定位到我们所需要的信息呢?

上节中的HTML代码可以看成具有树型的结构,如下图所示

HTML树结构

在这个树形结构中,每对标签可以看做是一个 节点 。比如 headh1父节点 ,而 h1head子节点。xpath就提供了一种简单的语法,来解析HTML的这种树型结构。常用的xpath选取节点的方式如下:

选取符号 含义
/ 从根节点开始选取
// 选择当前节点以下的节点,不考虑节点所处的绝对位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
text() 选取当前节点下的文字

我们来看一个实例:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>

<book>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>

</bookstore>

在上面这段XML代码中(如果读者不明白HTML和XML有什么差别,就把它们当成一样的好了),可以使用诸如如下的方式选取内容:

表达式 含义
/bookstore/book/author/text() 选取author节点下的文字内容
//author/text() 选取author节点下的文字内容
//titile[@lang=’en’]/text() 选取含有“lang”属性且属性值为“en”的title节点下的文字内容
//title[contains(@lang, ‘en’)]/text() 选取属性“lang”中包含“en”字符的title节点下的文字内容
//title/@lang 选取title的属性“lang”的值

以上是比较常用的xpath语法。

三、第一个爬虫

现在可以使用上面的工具,实现一个简单的爬虫了。我们以爬取豆瓣读书页面为例。

打开豆瓣读书的首页,可以看到有一个名为“最受关注图书榜”的栏目,栏目中共有十本书,以及与书相关的评论。我们的目的就是获取书名,以及评论。

豆瓣最受关注图书榜

在页面空白处点击鼠标右键,选择“检查元素”,可以在右方弹出检查页面元素的小窗口。

选择“检查元素”

把鼠标放在右侧的代码上,左侧就会用阴影区域显示出当前代码对应的内容。然后通过点击代码左侧折行的小三角,就可以一步一步定位到我们需要找的代码。

查看页面元素

通过检查我们可以发现,我们需要的信息处于这样的结构中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="section popular-books">
... ...
<li class>
... ...
<div class="info">
<h4 class="title"> XXX </h4>
... ...
<p class="reviews"> XXX </p>
... ...
</div>
... ...
</li>
... ...
</div>

不重要的部分我使用省略号略去,而“XXX”则代表我们需要提取出来的内容。使用xpath表示标题及评论内容为:

标题:
//div[@class='section popular-books']//li//div[@class='info']//h4[@class='title']//text()

评论:
//div[@class='section popular-books']//li//div[@class='info']//p[@class='reviews']//text()

需要注意的是,“<li>”标签表示的是列表项,因此我们可以先提取所有的“<li>”标签,然后在循环提取十个标题和评论就可以了。代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

# 导入需要的组件
from selenium import webdriver
from lxml import etree

driver = webdriver.Chrome()
driver.get('https://book.douban.com')

# page_source中存放的就是当前浏览器中显示页面的html代码
# 将其存放到page变量中
page = driver.page_source

# 使用etree中的HTML函数进行解析,并获取希望得到的信息
selector = etree.HTML(page)
info_list = selector.xpath("//div[@class='section popular-books']//li")

#循环获取标题和评论信息
titles = []
reviews = []
for info in info_list:
# 注意,下一行xpath语句以点开始
t = info.xpath(".//h4[@class='title']//text()")
titles.append(t)
# 注意,下一行xpath语句以点开始
r = info.xpath("//p[@class='reviews']//text()")
reviews.append(r)

# 保存到文件
with open("popular-books.txt", "w") as fw:
for i in range(len(titles)):
fw.write("书名:%s\n评论:%s\n" % (titles[i], reviews[i]))

这样,一个简单的爬虫就写好了。但是显然它还比较简陋,代码也比较丑,还有许多需要改进的地方。下一节将记录如何改进爬虫,使它变得更强大。

谢谢支持!
0%