“web抓取”是一个术语,即利用程序下载并处理来自web的内容。
▎在python中,有几个模块能让抓取网页变得很容易。
-
webbrowser:python自带,打开游览器获取指定页面。
-
requests:从因特网上下载文件和网页。
-
Beautiful Soup:解析HTML,即网页编写的格式。
-
selenium:启动并控制一个web游览器。selenium能够填写表单,并模拟鼠标在这个游览器中点击。
webbrowser模块
webbrowser模块的open()函数可以启动一个新游览器,打开指定的URL。
1
2
|
>>>
import
webbrowser
>>> webbrowser.
open
(
'http://www.baidu.com'
)
|
requests模块
requests模块能很容易从web下载文件,不必担心一些复杂的问题,诸如网络错误、连接问题和数据压缩。
requests模块不是python自带的,所以必须先通过pip安装。
编写requests模块是因为python的urllib2模块用起来太复杂。如果需要从web下载东西,还是使用requests模块更方便。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
[root@juispan ~]
# pip install requests
Collecting requests
Downloading requests-2.18.2-py2.py3-none-any.whl (88kB)
100% |████████████████████████████████| 92kB 75kB
/s
Collecting chardet<3.1.0,>=3.0.2 (from requests)
Downloading chardet-3.0.4-py2.py3-none-any.whl (133kB)
100% |████████████████████████████████| 143kB 75kB
/s
Collecting urllib3<1.23,>=1.21.1 (from requests)
Downloading urllib3-1.22-py2.py3-none-any.whl (132kB)
100% |████████████████████████████████| 133kB 21kB
/s
Collecting certifi>=2017.4.17 (from requests)
Downloading certifi-2017.7.27.1-py2.py3-none-any.whl (349kB)
100% |████████████████████████████████| 358kB 27kB
/s
Collecting idna<2.6,>=2.5 (from requests)
Downloading idna-2.5-py2.py3-none-any.whl (55kB)
100% |████████████████████████████████| 61kB 36kB
/s
Installing collected packages: chardet, urllib3, certifi, idna, requests
Successfully installed certifi-2017.7.27.1 chardet-3.0.4 idna-2.5 requests-2.18.2 urllib3-1.22
|
requests.get()函数接受一个要下载的URL字符串。通过在requests.get()的返回值上调用type(),返回一个Response对象,其中包含了web服务器对请求做出的响应。
通过检查Response对象的status_code属性,可以了解对这个网页的请求是否成功。如果该值等于requests.codes.ok,那么一切都好。
如果请求成功,下载的页面就作为一个字符串,保存在Response对象的text变量中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
>>>
import
requests
>>> res
=
requests.get(
'https://wkbos.bdimg.com/v1/wenku1//......=2017-07-30T13:23:41Z'
)
>>>
type
(res)
<
class
'requests.models.Response'
>
>>> res.status_code
=
=
requests.codes.ok
True
>>>
len
(res.text)
25663
>>>
print
(res.text[:
100
])
1.
阅读须知
文中使用
作为会命令行中的输出信息的前缀
对于不清楚用用途的函数可以在解释器下面输入
help
(函数名)来获取相关信息
另外,自带的文档和goo
|
除了status_code属性检查是否成功,还有一种简单的方法,就是在Response对象上调用raise_for_status()方法。如果下载文件出错,将抛出异常;如果下载成功,就什么也不做。
raise_for_status()方法是一种很好的方式,确保程序在下载失败时停止。可以用try和except语句将raise_for_status()代码包裹起来,处理这一错误,不让程序崩溃。
总是在调用requests.get()之后再调用raise_for_status()。确保下载确实成功,然后再让程序继续。
1
2
3
4
5
6
7
|
>>> res
=
requests.get(
'http://nostarch.com'
)
>>> res.raise_for_status()
Traceback (most recent call last):
File
"<stdin>"
, line
1
,
in
<module>
File
"/usr/lib/python2.7/site-packages/requests/models.py"
, line
937
,
in
raise_for_status
raise
HTTPError(http_error_msg, response
=
self
)
requests.exceptions.HTTPError:
403
Client Error: Forbidden
for
url: http:
/
/
nostarch.com
/
|
将下载的文件保存到硬盘,可以用标准的open()函数和write()方法,但必须用“写二进制(wb)”模式打开该文件,作为open()的第二参数。
即使该页面是纯文本的,也需要写入二进制数据,而不是文本数据,目的是为了保存该文本中的“unicode编码”。
为了将web页面写入到一个文件,可以用for循环和Response对象的iter_content()方法。
1
2
3
4
5
6
7
8
|
>>>
import
requests
>>> res
=
requests.get(
'http://www.gutenberg.org/cache/epub/1112/pg1112.txt'
)
>>> res.raise_for_status()
>>> playFile
=
open
(
'123.txt'
,
'wb'
)
>>>
for
text
in
res.iter_content(
100000
):
... playFile.write(text)
...
>>> playFile.close()
|
BeautifulSoup模块
HTML简介
超文本标记语言(HTML)是编写web页面的格式。
HTML中有许多不同的标签。有一些标签具有额外的特性,在尖括号内以“属性”的方式展现。
某些元素具有id属性,可以用来在页面上唯一地确定该元素。
程序可以根据元素的id属性来寻找它。开发者要弄清楚元素的id属性,这是编写web抓取程序常见的任务。
不要用正则表达式来解析HTML。在一个字符串中定位特定的一段HTML,这似乎很适合使用正则表达式。但是,不建议这么做。HTML的格式可以有很多不同的方式,并且仍然被认为是有效的HTML,但尝试用正则表达式来捕捉所有这些可能的变化,将非常繁琐,并且容易出错。专门用于解析HTML的模块,诸如Beautiful Soup,将更不容易导致缺陷。
Beautiful Soup是一个模块,用于从HTML页面中提取信息。它的名称是bs4,通过pip安装(pip install beautifulsoup4),导入使用命令import bs4。
针对要寻找的元素,调用method()方法,传入一个字符串作为CSS“选择器”。选择器就像正则表达式:它们指定了要寻找的模式。
1
2
3
4
5
6
|
[root@juispan ~]
# pip install beautifulsoup4
Collecting beautifulsoup4
Downloading beautifulsoup4-4.6.0-py2-none-any.whl (86kB)
100% |████████████████████████████████| 92kB 540kB
/s
Installing collected packages: beautifulsoup4
Successfully installed beautifulsoup4-4.6.0
|
1
2
3
4
5
6
|
>>>
import
requests,bs4
>>> res
=
requests.get(
'http://www.baidu.com'
)
>>> res.raise_for_status()
>>> bs
=
bs4.BeautifulSoup(res.text,
'html.parser'
)
>>>
type
(bs)
<
class
'bs4.BeautifulSoup'
>
|
▎CSS选择器举例:
soup.select('div') 所有名为<div>的元素
soup.select('#author') 带有id属性为author的元素
soup.select('p#author') 所有id属性为author的元素,只要它也在一个<p>元素之内
soup.select('.notice') 所有使用CSS class属性名为notice的元素
soup.select('div span') 所有在<div>元素之内的<span>元素
soup.select('div>span') 所有直接在<div>元素之内的<span>元素,中间没有其他元素
soup.select('input[name]') 所有名为<input>,并有一个name属性,其值无所谓的元素
soup.select('input[type="button"]') 所有名为<input>,并有一个type属性,其值为button的元素
不同的选择器模式可以组合起来,形成复杂的匹配。
select()方法将返回一个Tag对象的列表,这是Beautiful Soup表示一个HTML元素的方式。
针对BeautifulSoup对象的HTML的每次匹配,列表中都有一个Tag对象。
Tag值可以传递给str()函数,显示它们代表HTML标签。
Tag值也可以有attrs属性,它将该Tag的所有HTML属性作为一个字典。
Tag对象的get()方法让我们很容易从元素中获取属性值。向该方法传入一个属性名称的字符串,它将返回该属性的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
>>>
import
requests,bs4
>>> exampleFile
=
requests.get(
'http://www.baidu.com'
)
>>> exampleBS
=
bs4.BeautifulSoup(exampleFile.text,
'html.parser'
)
>>> elems
=
exampleBS.select(
'a'
)
>>>
type
(elems)
<
type
'list'
>
>>>
len
(elems)
11
>>>
type
(elems[
0
])
<
class
'bs4.element.Tag'
>
>>> elems[
0
].getText()
u
'\xe6\x96\xb0\xe9\x97\xbb'
>>>
str
(elems[
0
])
'<a class="mnav" href="http://news.baidu.com" name="tj_trnews">\xc3\xa6\xc2\x96\xc2\xb0\xc3\xa9\xc2\x97\xc2\xbb</a>'
>>> elems[
0
].attrs
{u
'href'
: u
'http://news.baidu.com'
, u
'name'
: u
'tj_trnews'
, u
'class'
: [u
'mnav'
]}
|
selenium模块
selenium模块让python直接控制游览器,实际点击链接,填写登录信息,几乎就像是有一个人类用户在与页面交互。
不推荐使用selenium模块下载文件,会有点慢,并且难以在后台运行。
selenium模块的导入方式:from selenium import webdriver。
使用FireFox方法,首先要在系统里安装火狐游览器。
1
2
3
4
5
6
|
[root@juispan ~]
# pip install selenium
Collecting selenium
Downloading selenium-3.4.3-py2.py3-none-any.whl (931kB)
100% |████████████████████████████████| 942kB 46kB
/s
Installing collected packages: selenium
Successfully installed selenium-3.4.3
|
1
2
3
4
5
|
>>>
from
selenium
import
webdriver
>>> browser
=
webdriver.FireFox()
>>>
type
(browser)
<
class
'selenium.webdriver.firefox.webdriver.WebDriver'
>
>>> browser.get(
'http://www.baidu.com'
)
|
WebDriver对象有好几种方法,用于在页面中寻找元素。它们被分成find_element_*和find_elements_*方法。
find_element_*方法返回一个WebElement对象,代表页面中匹配查询的第一个元素。
find_elements_*方法返回WebElement_*对象的列表,包含页面中所有匹配的元素。
▎WebDriver方法,用于寻找元素:
browser.find_element_by_class_name(name) 使用CSS类name的元素
browser.find_elements_by_class_name(name)
browser.find_element_by_css_selector(selector) 匹配CSS selector的元素
browser.find_elements_by_css_selector(selector)
browser.find_element_by_id(id) 匹配id属性值的元素
browser.find_elements_by_id(id)
browser.find_element_by_link_text(text) 完全匹配提供的text的<a>元素
browser.find_elements_by_link_text(text)
browser.find_element_by_partial_link_text(text) 包含提供的text的<a>元素
browser.find_elements_by_partial_link_text(text)
browser.find_element_by_name(name) 匹配name属性值的元素
browser.find_elements_by_name(name)
browser.find_element_by_tag_name(name) 匹配标签name的元素
browser.find_elements_by_tag_name(name) (大小写无关,<a>元素匹配‘a’和‘A’)
除了*_by_tag_name()方法,所有方法的参数都是区分大小写的。如果没有元素匹配,将会抛出NoSuchElement异常。
▎Webement的属性和方法:
tag_name 标签名,例如‘a’表示<a>元素
get_attribute(name) 该元素name属性的值
text 该元素内的文本,例如<span>hello</span>中的‘hello’
clear() 对于文本字段或文本区域元素,清除其中输入的文本
is_displayed() 如果该元素可见,返回True,否则返回False
is_enabled() 对于输入元素,如果该元素启用,返回True,否则返回False
is_selected() 对于复选框或单选框元素,如果该元素被选中,返回True,否则返回False
location 一个字典,包含键x和y,表示该元素在页面上的位置
find_element_*和find_elements_*方法返回的WebElement对象有一个click()方法,模拟鼠标在该元素上点击。
这个方法用于链接跳转,选择单选按钮,点击提交按钮,或者触发该元素被鼠标点击时发生的任何事情。
向web页面的文本字段发送击键,只要找到那个文本字段的<input>或<textarea>元素,然后调用send_keys()方法。
selenium有一个模块,针对不能用字符串值输入的键盘击键。它的功能非常类似转义字符。
这些值保存在selenium.webdriver.common.keys模块的属性中。由于这个模块名较长,建议from selenium.webdriver.common.keys import Keys。
▎selenium.webdriver.common.keys模块中常用的变量:
Keys.DOWN,Keys.UP,Keys.LEFT,Keys.RIGHT 键盘箭头键
Keys.ENTER,Keys.RETURN 回车和换行键
Keys.HOME,Keys.END,Keys.PAGE_DOWN,Keys.PAGE_UP HOME键、END键、Page Up键、Page Down键
Keys.ESCAPE,Keys.BACK_SPACE,Keys.DELETE Esc、Backspace和Delete键
Keys.F1,Keys.F2,...,Keys.F12 键盘顶部的F1到F12键
Keys.TAB Tab键
▎利用以下的方法,selenium也可以模拟点击各种游览器按钮:
-
browser.back()点击“返回”按钮。
-
browser.forward()点击“前进”按钮。
-
browser.refresh()点击“刷新”按钮。
-
browser.quit()点击“关闭窗口”按钮。