“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()点击“关闭窗口”按钮。