目录

用Python写网络爬虫(2)-数据抓取

数据抓取

从网页中抽取数据用于后续处理,这种做法称为抓取(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())