python爬虫读书笔记——数据抓取(4)

三种网页抓取方法

1.正则表达式

(这个真的很难懂,之后会单独写篇笔记)

2.Beautiful Soup

该模块可以解析网页,并提供定位内容的便捷接口。

3.Lxml

lxml是基于libxml2这一lxml解析库的python封装。该模块使用C语言编写,解析速度比Beautiful Soup更快。

lxml也可以正确解析属性两侧缺失的引号,并闭合标签,不过该模块没有额外添加<html>和<body>标签。

解析完输入内容之后,进入选择元素的步骤,此时lxml 有几种不同的方法,比如XPath 选择器和类似Beautiful Soup的find()方法。 不过, 我们将会使用 css选择器, 因为它更加简沽, 并且能够在解析动态内容时得以复用。、

性能对比 

FIELDS=('area','population','iso','country','capital','continent ', 'tld','currency code ','currency name','phone','postal_code_format','postal_code_regex','languages' ,'neighbours')

import re
def re_scraper(html):
    results={}
    for field in FIELDs:
        results[field]=re.search('<tr id="place_%s_row">.*?<td class="w2p_fw">(.*?)<td class="w2p_fw">(.*?)</td>'
                                 % field,html).groups()[0]
    return results

from bs4 import BeautifulSoup
def bs_scraper(html):
    soup=BeautifulSoup(html,'html.parser')
    results={}
    for field in FIELDS:
        results[field]=soup.find('table').find('tr',id='places_%s_row' %field).find('td',class_='w2p_fw').text
    return results

import lxml.html
def lxml_scraper(html):
    tree=lxml.html.fromstring(html)
    results={}
    for field in FIELDS:
        results[field]=tree.csselect('table>tr#palce_%s_row>td.w2p_fw' %field)[0].text_content()
    return results

抓取结果:

import  time
NUM_ITERATIONS=1000 # 测试每次抓取的时间次数
html=download('http://example.webscraping.com/places/view/United-Kingdom-239')
for name,scraper in [('Regular expressions',re_scraper),('BeautifulSoup',bs_scraper),('lxml',lxml_scraper)]:
    #记录开始抓取的时间
    for i in range(NUM_ITERATIONS):
        if scraper==re_scraper:
            re.purge()
        result=scraper(html)
        #检查期待的抓取结果
        assert(result['area']=='244,820 square kilometres')
    #record end time of scrape and output the total
    end=time.time()
    print('%s: %.2f seconds' %(name,end-start))

在这段代码中, 每个爬虫都会执行1000 次, 每次执行都会检查抓取结果 是否正确, 然后打印总用时。 这里使用的 download 函数依然是上一章中定 义的那个函数。 请注意, 我们在加粗的代码行中调用了 re.purge ()方法。 默认情况下, 正则表达式模块会缓存搜索结果,为了与其他爬虫的对比更加 公平, 我们需要使用该方法清除缓存。

下面是在电脑中运行该脚本的结果。
$ python performance.py

Regular expressions: 5.50 seconds

BeautifulSoup : 42.84 seconds

Lxml : 7.06 seconds

结论:

抓取方式性能使用难度安装难度
正则表达式困难简单(内置模块)
Beautiful Soup简单简单(内置模块)
Lxml简单相对困难

如果你的爬虫瓶颈是下载网页, 而不是抽取数据的话,那么使用较慢的方 法(如 Beautiful Soup) 也不成问题。

如果只需抓取少量数据, 并且想要避免额外依赖的话, 那么正则表达式可能更加适合。

不过,通常情况下, lxml 是 抓取数据的最好选择,这是因为该方法既快速又健壮,而正则表达式和 Beautiful Soup只在某些特定场景下有用。

为链接爬虫抓取回调

前面我们已经了解了如何抓取数据, 接下来我们需要将其集成到上一章的链接爬虫当中。 要想复用这段爬虫代码抓取其他网站, 我们需要添 加一个callback参数处理抓取行为。callback是一个函数,在发生某 个特定事件之后会调用该函数 (在本例中, 会在网页下载完成后调用 )。该抓取 callback 函数包含 url 和 html 两个参数, 并且可以返回一个待爬 取的 URL 列表。下面是其实现代码,可以看出在Python中实现该功能非常简单。

def link_crawler(...,scraper_callback-None):
    ...
    links=[]
    if scrape_callback:
        links.extend(scrape_callback(url,html)or [])
        ...

抓取数据并显示出来:

def scrape_callback(url,html):
    if re.search('/view/',url):
        tree=lxml.html.fromstring(html)
        row=[tree.cssselect('table>tr#places_%s_row >td.w2p_fw' %field)[0].text_content() for field in FIELDS]
        print url,row

扩展功能,把得到的结果保存到csv表格中:

import csv
class ScrapeCallback:
    def __init__(self):
        self.writer=csv.writer(open('countries.csv','w'))
        self.fields=('area','population','iso','country','capital','continent','tld','currency_code',
                     'currency_name','phone','postal_code_format','postal code regex', 'languages','neighbours')
        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)

为了实现该 callback,我们使用了回调类,而不再是回调函数,以便保 持csv 中 writer 属性的状态。csv 的 writer 属性在构造方法中进行了实例化处理,然后在_call_方法中执行了多次写操作。 请注意,_call_是一个特殊方法,在对象作为函数被调用时会调用该方法,这也是链接爬虫中 cache_callback的调用方法。也就是说,scrape_callback (url, html) 和调用 scrape_callback. _call_(url, html)是等价的。 

向链接爬虫传入回调的代码写法:

link_crawler('http://example.webscraping.com/ ','/(Index|view)',max_depth=-1,scrape_callback=ScrapeCallback ()) 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值