python网络爬虫——数据抓取
我们需要让爬虫从每个网页中抽取一些数据,然后实现某些事情,这种做法被称为抓取。
分析网页
查看网页源代码,使用Firebug Lite扩展,Firebug是Joe Hewitt开发的一套与Firefox集成在一起的功能强大的web开发工具,可以实时编辑、调试和监测任何页面的CSS、HTML和JavaScript。在这里用于网页源代码的查看。
安装Firebug Lite,下载Firebug Lite的包,然后再浏览器中安装这个插件。
三种网页抓取的方法
正则表达式、BeatifulSoup模板、强大的lxml模块
正则表达式
爬取第一页所有国家的国土面积
def download(url,user_agent='wswp',proxy=None,num_retries=2):
print 'Downloading:',url
headers={'User-agent':user_agent}
request=urllib2.Request(url,headers=headers)
opener=urllib2.build_opener()
if opener:
proxy_params={urlparse.urlparse(url).scheme:proxy}
opener.add_handler(urllib2.ProxyHandler(proxy_params))
try:
html=urllib2.urlopen(request).read()
except urllib2.URLError as e:
print 'Download:' ,e.reason
html=None
if num_retries>0:
if hasattr(e,'code') and 500 <=e.code<600:
return download(url,num_retries-1)
return html
def get_links(html):
webpage_regx=re.compile('<a[^>]+href=["\'](.*?)["\']',re.IGNORECASE)
# webpage_regx=re.compile(,re.IGNORECASE)
link_list1=webpage_regx.findall(html)
link_list2=[]
for link in link_list1:
if re.search('/view/',link):
link_list2.append(link)
return link_list2
import re
url='http://example.webscraping.com/view/United-Kingdom-239'
html=download(url)
for link in get_links(html):
link = urlparse.urljoin(url, link)
areas=download(link)
# print areas
#匹配国家面积
context=re.findall(r'<td class="w2p_fw">(.*?)</td>',areas)[1]
print context
运行结果:
正则表达式提供了抓取数据的快捷方式,但是该方法过于脆弱,容易在网页更新后出现问题。
Beautful Soup
Beautful Soup是一个非常流行的Python模块。该模块可以解析网页,并提供定位内容的便捷接口。
安装
pip install beautifulsoup4 --user
使用第一步:将已经下载的HTML内容解析为soup文档。
使用find或者find_all方法获取.
Lxml
基于libxml2这一XML解析库的Python封装.该模块使用C编写,解析速度更快.
第一步:将有可能步合法的HTML解析为统一格式.
import lxml.html
broken_html='<ul class=country><li>Area<li>Population</ul>'
tree=lxml.html.fromstring(broken_html)
fixed_html=lxml.html.tostring(tree,pretty_print=True)
print fixed_html
"""
<ul class="country">
<li>Area</li>
<li>Population</li>
</ul>
"""
测试三种方法的性能
import re
import urllib2
import urlparse
from bs4 import BeautifulSoup
import lxml.html
import time
#
#
#
#获取网页内容
def download(url,user_agent='wswp',proxy=None,num_retries=2):
print 'Downloading:',url
headers={'User-agent':user_agent}
request=urllib2.Request(url,headers=headers)
opener=urllib2.build_opener()
if opener:
proxy_params={urlparse.urlparse(url).scheme:proxy}
opener.add_handler(urllib2.ProxyHandler(proxy_params))
try:
html=urllib2.urlopen(request).read()
except urllib2.URLError as e:
print 'Download:' ,e.reason
html=None
if num_retries>0:
if hasattr(e,'code') and 500 <=e.code<600:
return download(url,num_retries-1)
return html
#使用正则表达式匹配
def re_scraper(html):
results={}
results['area']=re.search('<tr id="places_area__row">.*?<td class="w2p_fw">(.*?)</td>',html).groups()[0]
return results
#使用BeautifulSoup匹配
def bs_scraper(html):
soup=BeautifulSoup(html)
results={}
results['area']=soup.find('table').find('tr',id='places_area__row').find('td', class_='w2p_fw').string
return results
#使用cssselect选择器匹配
def lxml_scraper(html):
tree=lxml.html.fromstring(html)
results={}
conf=tree.cssselect('table > tr#places_area__row > td.w2p_fw')[0].text_content()
results['area']=conf
return results
#计算获取时间
#每个网站爬取的次数
NUM_ITERATIONS=1000
html=download('http://example.webscraping.com/places/default/view/Afghanistan-1')
for name,scraper in [('Re',re_scraper),('Bs',bs_scraper),('Lxml',lxml_scraper)]:
#开始的时间
start=time.time()
for i in range(NUM_ITERATIONS):
if scraper==re_scraper:
#默认情况下,正则表达式模块会缓存搜索结果,为了使对比条件更一致,re.purge()方法清除缓存
re.purge()
result=scraper(html)
#检查结果
assert(result['area']=='647,500 square kilometres')
#结束时间
end=time.time()
print '%s: %.2f seconds' %(name,end-start)
结果分析:
由于lxml和正则表达式都是用C语言写的,所以效果比用Python写的BeautifulSoup要好.由于lxml在搜索之前必须输入解析为内部格式,所以会产生额外的开销.而爬取同一网页时这种开销会降低.
方法总结
抓取方法 | 性能 | 使用难度 | 安装难度 |
---|---|---|---|
正则表达式 | 快 | 困难 | 简单(内置) |
Beautiful Soup | 慢 | 简单 | 简单(纯python) |
Lxml | 快 | 简单 | 相对困难 |
如果是下在网页,而不是抽取数据的话,那么使用Beautiful Soup,如果只需抓取少量数据,并且避免额外依赖的话,选择正,通常情况下使用lxml比较合适.
上一篇: 对称矩阵及对称矩阵的压缩存储