数据抓取
从网页中抽取数据用于后续处理,这种做法称为抓取(scraping)。
正则表达式
- 优点:能够简短地把需要的数据抓取出来。在一次性数据抓取中非常有用,此外还可以避免解析整个网页带来的开销;
- 缺点:网页更新后易失效,健壮的正则表达式又存在难以构造、可读性差的问题。
Beautifulsoup
漂亮汤是一个非常流行的Python模块。该模块可以解析网页,并提供定位内容的便捷接口。
- 可以正确解析缺失的引号并闭合标签(html.parser);
- 可以使用正则表达式;
- 可以使用css选择器;
- 可以使用lxml作为解析器(谁还说我慢!);
注意:查询得到的对象并不是字符串,而是bs4库的element对象。
Lxml
使用C语言编写,就是快,不过安装比较麻烦。
- 可以正确解析缺失的引号并闭合标签(html.parser);
- 可以使用XPath选择器;
- 可以使用CSS选择器;
为链接爬虫添加抓取回调
为爬虫函数添加一个callback参数处理抓取行为。
1
2
3
4
5
6
| def crawler(..., scrape_callback=None):
...
links = []
if scrape_callback:
# 利用抓取到的内容更新url列表
links.extend(scrape_callback(url, html) or [])
|
使用类的魔术方法__call__()
来构造回调类,回调类的对象是可执行的。(是否可执行使用内置函数callable()
来判断。下面是一个抓取网站/view/xxx
下的信息,并保存到csv文件中的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import csv
import re
class ScrapeCallback:
def __init__(self):
self.writer = csv.writer(open('countries.csv', 'w'))
self.fields = ('area', 'population', 'iso', 'country', ...)
self.writer.writerow(self.fields)
def __call__(self, url, html):
if re.search('/view/', url):
tree = lxml.html.fromstring(html)
row = []
for field in self.fields:
row.append(tree.cssselect('table > tr#places_{}__row > td.w2p_fw'.format(field))[0].text_content())
self.writer.writerow(row)
|
向链接爬虫传入回调:
1
| crawler('http://example.webscraping.com/', '/(index|view)', max_depth=-1, scrape_callback=ScrapeCallback())
|