php可以抓取网页数据吗(正则表达式抓取国家面积数据的解决方案)

优采云 发布时间: 2021-09-19 23:07

  php可以抓取网页数据吗(正则表达式抓取国家面积数据的解决方案)

  接下来,我们将介绍三种获取网页数据的方法,首先是正则表达式,然后是流行的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只在某些特定场景中有用

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线