目标:
掌握三种网页抓取的方法:正则表达式、BeautifulSoup模块,lxml模块
1.正则表达式
首先确定目标网页:http://example.webscraping.com/view/United-Kingdom-239,抓取目标:网页中的面积数据
import urllib
from urllib.request import urlopen
import re
import urllib.error
def download(url):
print('Downloading:',url)
try:
request=urllib.request.Request(url)
response=urllib.request.urlopen(request)
html=response.read().decode('utf-8')
except urllib.error.URLError as e:
print('Download error:',e.reason)
html=None
return html
url='http://example.webscraping.com/view/United-Kingdom-239'
html=download(url)
# print(html)
pattern=re.findall('<td class="w2p_fw">(.*?)</td>',html)
print(pattern)
这样的代码得到的部分结果是:
['<img src="/places/static/images/flags/gb.png" />', '244,820 square kilometres', '62,348,447', 'GB', 'United Kingdom', 'London', '<a href="/continent/EU">EU</a>', '.uk', 'GBP', 'Pound', '44', '@# #@@|@## #@@|@@# #@@
可以看到多个国家属性都用到了'<td class="w2p_fw">标签,要想分离出面积属性,我们可以只选择其中的第二个元素,如下所示:
pattern=re.findall('<td class="w2p_fw">(.*?)</td>',html)[1]
此时得到的结果:
244,820 square kilometres
虽然现在可以使用这个方案,但是如果网页发生变化,该方案很可能就会失效。比如表格发生了变化,去除了第二行中的国土面积数据。如果我们只是现在抓取数据,就可以忽略这种未来可能发生的变化,但是,如果我们希望未来还能再次抓取该数据,就需要给出更加健壮的解决方案,从而尽可能避免这种布局变化所带来的问题。想要该正则表达式更加健壮, 我们可以将其父元素<tr>也加入进来。由于该元素具有ID属性,所以应该是唯一的。
pattern=re.findall('<tr id="places_area__row">.*?<td class="w2p_fw">(.*?)</td>',html)
可以得到相同的结果
虽然这个正则表达式更容易适应未来的变化,但又存在着难以构造、可读性差的问题,此外,还有一些微小的布局变化也会使该正则表达式无法满足,过于脆弱,所以接下来介绍另外两种网页抓取的方法。
2.Beautiful Soup
Beautiful Soup是一个非常流行的Python模块。该模块可以解析网页,并提供定位内容的便捷接口。
使用Beautiful Soup的第一步是将已下载的HTML内容解析为soup文档。由于大多数网页都不具备良好的HTML格式,因此Beautiful soup需要对其实际格式进行确定。举个例子,用Beautiful Soup来解析属性值两侧引号缺失和标签未闭合的问题。
from bs4 import BeautifulSoup
broken_html='<ul class=country><li>Area<li>Population</ul>'
#解析HTML
soup=BeautifulSoup(broken_html,'lxml')
fixed_html=soup.prettify()
#可以用prettify实现格式化输出,使HTML标准
print(fixed_html)
运行结果:
<html>
<body>
<ul class="country">
<li>
Area
</li>
<li>
Population
</li>
</ul>
</body>
</html>
从上面的执行结果我们可以看出,Beautiful Soup能够正确解析缺失的引号并闭合标签,此外还添加了<html>和<body>标签使其成为完整的HTML文档。现在可以使用find()和find_all()方法来定位我们需要的元素了。
#运行结果from bs4 import BeautifulSoup broken_html='<ul class=country><li>Area<li>Population</ul>' #解析HTML soup=BeautifulSoup(broken_html,'lxml') fixed_html=soup.prettify() #可以用prettify实现格式化输出,使HTML标准 ul=soup.find('ul',attrs={'class':'country'}) print(ul.find('li')) #打印出了匹配的第一个 print(ul.find_all('li')) #打印出了所有匹配到的
<li>Area</li> [<li>Area</li>, <li>Population</li>]
#接下来,我们使用这个方法来抽取示例国家面积的完整数据: