php可以抓取网页数据吗(正则表达式抓取国家面积数据的解决方案)
优采云 发布时间: 2021-09-19 23:07php可以抓取网页数据吗(正则表达式抓取国家面积数据的解决方案)
接下来,我们将介绍三种获取网页数据的方法,首先是正则表达式,然后是流行的Beauty soup模块,最后是功能强大的lxml模块
1.正则表达式
如果您不熟悉正则表达式或需要一些技巧,可以参考正则表达式如何获得完整的介绍
当我们使用正则表达式捕获国家区域数据时,必须首先尝试匹配元素的内容,如下所示:
>>> import re
>>> import urllib2
>>> url = \'http://example.webscraping.com/view/United-Kingdom-239\'
>>> html = urllib2.urlopen(url).read()
>>> re.findall(\'(.*?)\', html)
[\'/places/static/images/flags/gb.png\', \'244,820 square kilometres\', \'62,348,447\', \'GB\', \'United Kingdom\', \'London\', \'EU\', \'.uk\', \'GBP\', \'Pound\', \'44\', \'@# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA\', \'^(([A-Z]\\d{2}[A-Z]{2})|([A-Z]\\d{3}[A-Z]{2})|([A-Z]{2}\\d{2}[A-Z]{2})|([A-Z]{2}\\d{3}[A-Z]{2})|([A-Z]\\d[A-Z]\\d[A-Z]{2})|([A-Z]{2}\\d[A-Z]\\d[A-Z]{2})|(GIR0AA))$\', \'en-GB,cy-GB,gd\', \'IE \']
>>>
从以上结果可以看出,多个国家属性使用标签。要分离区域属性,我们只能选择第二个元素,如下所示:
>>> re.findall(\'(.*?)\', html)[1]
\'244,820 square kilometres\'
虽然现在可以使用此方案,但如果网页发生更改,则可能会失败。例如,表格已更改,删除了第二行中的土地面积数据。如果我们现在只捕获数据,我们可以忽略未来可能发生的变化。但是,如果我们希望在将来再次捕获数据,我们需要提供一个更健壮的解决方案,以尽可能避免这种布局更改的影响。为了使正则表达式更健壮,我们可以添加它的父元素。元素应该是唯一的,因为它有一个ID属性
>>> re.findall(\'Area: (.*?)\', html)
[\'244,820 square kilometres\']
这个迭代版本看起来更好,但是还有许多其他方法来更新网页,这也会使正则表达式不满意。例如,将双引号更改为单引号,在标签之间添加额外的空格,或更改区域标签等。以下是尝试支持这些可能性的改进版本
>>> re.findall(\'.*?>> from bs4 import BeautifulSoup
>>> broken_html = \'AreaPopulation\'
>>> # parse the HTML
>>> soup = BeautifulSoup(broken_html, \'html.parser\')
>>> fixed_html = soup.prettify()
>>> print fixed_html
Area
Population
从上面的执行结果可以看出,beautiful soup可以正确解析缺少的引号并关闭标签。现在可以使用find()和find_uAll()方法来定位我们需要的元素
>>> ul = soup.find(\'ul\', attrs={\'class\':\'country\'})
>>> ul.find(\'li\') # return just the first match
AreaPopulation
>>> ul.find_all(\'li\') # return all matches
[AreaPopulation, Population]
注意:由于不同版本的Python内置库的容错性不同,处理结果可能与上述不同。请参阅:。有关所有方法和参数,请参阅beautiful soup的官方文档
以下是使用此方法提取样本国家/地区数据的完整代码
>>> from bs4 import BeautifulSoup
>>> import urllib2
>>> url = \'http://example.webscraping.com/view/United-Kingdom-239\'
>>> html = urllib2.urlopen(url).read()
>>> # locate the area row
>>> tr = soup.find(attrs={\'id\':\'places_area__row\'})
>>> # locate the area tag
>>> td = tr.find(attrs={\'class\':\'w2p_fw\'})
>>> area = td.text # extract the text from this tag
>>> print area
244,820 square kilometres
尽管此代码比正则表达式代码更复杂,但它更易于构造和理解。此外,我们不需要担心布局中的小变化,例如冗余空间和标签属性
3.Lxml
Lxml是一个基于libxml2的python包,libxml2是一个XML解析库。该模块是用C语言编写的,解析速度比beautiful soup快,但安装过程也比较复杂。请参阅最新的安装说明**
与Beauty soup一样,使用lxml模块的第一步是将可能非法的HTML解析为统一格式。以下是使用此模块解析不完整HTML的示例:
>>> import lxml.html
>>> broken_html = \'AreaPopulation\'
>>> # parse the HTML
>>> tree = lxml.html.fromstring(broken_html)
>>> fixed_html = lxml.html.tostring(tree, pretty_print=True)
>>> print fixed_html
Area
Population
类似地,lxml可以正确解析属性和close标记周围缺少的引号,但模块不添加和标记
解析输入内容后,进入选择元素的步骤。此时,lxml有几种不同的方法,例如XPath选择器和类似于beautiful soup的find()方法。不过,我们稍后将使用CSS选择器,因为它更简洁,在解析动态内容时可以重用。此外,一些有jQuery选择器经验的读者会更熟悉它
以下是使用lxml的CSS选择器提取区域数据的示例代码:
>>> import urllib2
>>> import lxml.html
>>> url = \'http://example.webscraping.com/view/United-Kingdom-239\'
>>> html = urllib2.urlopen(url).read()
>>> tree = lxml.html.fromstring(html)
>>> td = tree.cssselect(\'tr#places_area__row > td.w2p_fw\')[0] # *行代码
>>> area = td.text_content()
>>> print area
244,820 square kilometres
*第一行代码将查找具有ID_uu区域_uu行的位置,然后为FW选择类W2P_uu表数据子标签
CSS选择器表示用于选择元素的模式。以下是选择器的一些常见示例:
选择所有标签: *
选择 标签: a
选择所有 class="link" 的元素: .link
选择 class="link" 的 标签: a.link
选择 id="home" 的 标签: a#home
选择父元素为 标签的所有 子标签: a > span
选择 标签内部的所有 标签: a span
选择 title 属性为"Home"的所有 标签: a[title=Home]
W3C已经在
Lxml实现了大多数CSS3属性。有关不支持的函数,请参阅:
注意:在lxml的内部实现中,它实际上将CSS选择器转换为等效的XPath选择器
4.性能比较
在下面的代码中,每个爬虫程序将执行1000次。每次执行将检查爬网结果是否正确,然后打印总时间
# -*- coding: utf-8 -*-
import csv
import time
import urllib2
import re
import timeit
from bs4 import BeautifulSoup
import lxml.html
FIELDS = (\'area\', \'population\', \'iso\', \'country\', \'capital\', \'continent\', \'tld\', \'currency_code\', \'currency_name\', \'phone\', \'postal_code_format\', \'postal_code_regex\', \'languages\', \'nei*敏*感*词*ours\')
def regex_scraper(html):
results = {}
for field in FIELDS:
results[field] = re.search(\'.*?(.*?)\'.format(field), html).groups()[0]
return results
def beautiful_soup_scraper(html):
soup = BeautifulSoup(html, \'html.parser\')
results = {}
for field in FIELDS:
results[field] = soup.find(\'table\').find(\'tr\', id=\'places_{}__row\'.format(field)).find(\'td\', class_=\'w2p_fw\').text
return results
def lxml_scraper(html):
tree = lxml.html.fromstring(html)
results = {}
for field in FIELDS:
results[field] = tree.cssselect(\'table > tr#places_{}__row > td.w2p_fw\'.format(field))[0].text_content()
return results
def main():
times = {}
html = urllib2.urlopen(\'http://example.webscraping.com/view/United-Kingdom-239\').read()
NUM_ITERATIONS = 1000 # number of times to test each scraper
for name, scraper in (\'Regular expressions\', regex_scraper), (\'Beautiful Soup\', beautiful_soup_scraper), (\'Lxml\', lxml_scraper):
times[name] = []
# record start time of scrape
start = time.time()
for i in range(NUM_ITERATIONS):
if scraper == regex_scraper:
# the regular expression module will cache results
# so need to purge this cache for meaningful timings
re.purge() # *行代码
result = scraper(html)
# check scraped result is as expected
assert(result[\'area\'] == \'244,820 square kilometres\')
times[name].append(time.time() - start)
# record end time of scrape and output the total
end = time.time()
print \'{}: {:.2f} seconds\'.format(name, end - start)
writer = csv.writer(open(\'times.csv\', \'w\'))
header = sorted(times.keys())
writer.writerow(header)
for row in zip(*[times[scraper] for scraper in header]):
writer.writerow(row)
if __name__ == \'__main__\':
main()
注意,我们在*代码中调用了re.purge()方法。默认情况下,正则表达式缓存搜索结果。为了公平起见,我们需要使用此方法清除缓存
以下是在我的计算机上运行脚本的结果:
由于硬件条件的不同,不同计算机的执行结果也会不同。然而,每种方法之间的相对差异应该相当大。从结果可以看出,在捕获我们的示例网页时,Beauty soup的速度比其他两种方法慢7倍多。事实上,这个结果是意料之中的,因为lxml和正则表达式模块是用C语言编写的,而Beauty soup是用纯python编写的。一个有趣的事实是,lxml的行为几乎和正则表达式一样好。由于lxml必须在搜索元素之前将输入解析为内部格式,因此会产生额外的开销。当捕获同一网页的多个功能时,初始化和解析的开销将减少,lxml将更具竞争力。因此,lxml是一个功能强大的模块
5.摘要
三种网页捕获方法的优缺点:
抓取方法 演出 使用困难 安装困难
正则表达式
来吧
困难
简单(内置模块)
靓汤
慢
简单的
简单(纯Python)
Lxml
来吧
简单的
相对困难
如果您的爬虫瓶颈是下载网页而不是提取数据,那么使用较慢的方法(如BeautifulSoup)并不是问题。正则表达式在一次性提取中非常有用。此外,它们还可以避免解析整个网页的开销。如果您只需要获取少量数据并希望避免额外的依赖项,则正则表达式可能更合适。但是,一般来说,lxml是抓取数据的最佳选择,因为它不仅速度快,而且功能更强大,而正则表达式和Beauty soup只在某些特定场景中有用