跟着高手学习 Scrapy 技巧:第一部分
Scrapy 是 Scrapinghub 的关键部分。我们广泛地采用此框架,并已积累了许多各种不同的快捷方法来解决常见问题。我们推出了一个系列来与大家分享这些 Scrapy 的技巧,这样,你就可以在你的日常工作流程中最有效的使用它。每一个博文将给出两到三个提示,敬请关注。
使用 Extruct 从网站中提取微观数据(Microdata)
我相信网络爬虫的每一个开发者都会有理由来咒骂那些对他们的网站使用凌乱的布局的 Web 开发者。没有语义标记的网站,特别是那些基于 HTML 表格的网站,绝对是槽糕透顶。这些类型的网站使得爬取更加困难,因为几乎没有关于每一个元素代表什么的提示。有时候,你甚至不得不相信每个页面上的元素顺序将保持不变,从而抓取你需要的数据。
这就是为什么我们如此感激 Schema.org , 共同努力来使得语义标记在网页上。该项目为 Web 开发者提供了在他们的网站上展示一定范围的不同对象(包括 Person, Product, 和 Review)的架构,并使用例如 Microdata , RDFa , JSON-LD 等的任何元数据格式。这使得搜索引擎工作更加容易,因为它们可以从网站上提取有用信息,而不必深入到他们所抓取网站的 HTML 结构中。
例如, AggregateRating 是网上零售商用来展示他们产品的用户评级的架构。下面是描述一个使用 Microdata format 的网上商店中的一个产品的用户评级的标记:
<div itemprop="aggregateRating" itemscope="" itemtype="http://schema.org/AggregateRating">
<meta itemprop="worstRating" content="1">
<meta itemprop="bestRating" content="5">
<div class="bbystars-small-yellow">
<div class="fill" style="width: 88%"></div>
</div>
<span itemprop="ratingValue" aria-label="4.4 out of 5 stars">4.4</span>
<meta itemprop="reviewCount" content="305733">
</div>
通过这种方式,搜索引擎可以在搜索结果中同时展示一个产品的评级及其 URL,而不需要为每一个网站编写特定的爬虫:
你还可以受益于一些网站使用的语义标记。我们推荐使用 Extruct , 一个从 HTML 文档中中提取 嵌入式元数据 的库。它分析整个 HTML 并返回一个包含微观数据(microdata)的 Python 字典。看看我们是如何用它来提取展示用户评级的微观数据的:
>>> from extruct.w3cmicrodata import MicrodataExtractor
>>> mde = MicrodataExtractor()
>>> data = mde.extract(html_content)
>>> data
{
'items': [
{
'type': 'http://schema.org/AggregateRating',
'properties': {
'reviewCount': '305733',
'bestRating': '5',
'ratingValue': u'4.4',
'worstRating': '1'
}
}
]
}
>>> data['items'][0]['properties']['ratingValue']
u'4.4'
现在,让我们建立一个使用 Extruct 的爬虫,它从 苹果产品网站 上提取价格和评级。 该网站使用了微观数据来存储所列出的产品信息。它为每个产品使用这个结构 T:
<div itemtype="http://schema.org/Product" itemscope="itemscope">
<img src="/images/MLA02.jpg" itemprop="image" />
<a href="/shop/product/MLA02/magic-mouse-2?" itemprop="url">
<span itemprop="name">Magic Mouse 2</span>
</a>
<div class="as-pinwheel-info">
<div itemprop="offers" itemtype="http://schema.org/Offer" itemscope="itemscope">
<meta itemprop="priceCurrency" content="USD">
<span class="as-pinwheel-pricecurrent" itemprop="price">
$79.00
</span>
</div>
</div>
</div>
有了这个设置,你不需要使用 XPath 或者 CSS 选择器来提取所需数据。你只需要在你的爬虫中使用 Extruct 的 MicrodataExtractor:
import scrapy
from extruct.w3cmicrodata import MicrodataExtractor
class AppleSpider(scrapy.Spider):
name = "apple"
allowed_domains = ["apple.com"]
start_urls = (
'http://www.apple.com/shop/mac/mac-accessories',
)
def parse(self, response):
extractor = MicrodataExtractor()
items = extractor.extract(response.body_as_unicode(), response.url)['items']
for item in items:
if item.get('properties', {}).get('name'):
properties = item['properties']
yield {
'name': properties['name'],
'price': properties['offers']['properties']['price'],
'url': properties['url']
}
此爬虫会生成这样的项:
{
"url": "http://www.apple.com/shop/product/MJ2R2/magic-trackpad-2?fnode=4c",
"price": u"$129.00",
"name": u"Magic Trackpad 2"
}
所以,当你爬取的网站使用微观数据来将语义信息添加到它的内容中时,使用 Extruct 。这是一个比依赖统一的页面布局或者浪费时间分析 HTML 源代码更健壮的解决方案。
使用 js2xml 抓取嵌入在 JavaScript 代码段中的数据
你是否曾经受挫于你的浏览器呈现的网页与 Scrapy 下载的网页之间的差距?这可能是因为该网页中的一些内容并不在服务器发送给你的响应中。相反,它们是由你的浏览器通过 JavaScript 代码生成的。
你可以通过将此请求传递给一个例如 Splash 的 JavaScript 渲染服务来解决此问题。 Splash 运行页面上的 JavaScript,然后返回最终的页面结构以供你的爬虫使用。
Splash 专门为此设计,并 与 Scrapy 很好的整合在一起 。然而,在某些情况下,你需要的是一些简单功能,例如从一个 JavaScript 段中获取一个变量的值,所以使用这种强大的工具将大材小用。而这恰恰是 js2xml 的用武之地。它是一个将 JavaScript 代码转换成 XML 数据的库。
例如,假设一个在线零售商网站通过 JavaScript 加载产品评级。混合在该 HTML 中有这样一段 JavaScript 代码:
<script type="text/javascript">
var totalReviewsValue = 32;
var averageRating = 4.5;
if(totalReviewsValue != 0){
events = "...";
}
...
</script>
要使用 js2xml 提取 averageRating
的值,我们首选需要提取 <script>
块,然后使用 js2xml 将此代码转换成 XML:
>>> js_code = response.xpath("//script[contains(., 'averageRating')]/text()").extract_first()
>>> import js2xml
>>> parsed_js = js2xml.parse(js_code)
>>> print js2xml.pretty_print(parsed_js)
<program>
<var name="totalReviewsValue">
<number value="32"/>
</var>
<var name="averageRating">
<number value="4.5"/>
</var>
<if>
<predicate>
<binaryoperation operation="!=">
<left><identifier name="totalReviewsValue"/></left>
<right><number value="0"/></right>
</binaryoperation>
</predicate>
<then>
<block>
<assign operator="=">
<left><identifier name="events"/></left>
<right><string>...</string></right>
</assign>
</block>
</then>
</if>
</program>
现在,只需要建立一个 Scrapy 的 Selector,然后使用 XPath 获取我们想要的值:
>>> js_sel = scrapy.Selector(_root=parsed_js)
>>> js_sel.xpath("//program/var[@name='averageRating']/number/@value").extract_first()
u'4.5'
虽然你可能用思考的速度就可以编写一个正则表达式来解决这个问题,但是,一个 JavaScript 解析器将会更可靠。我们这里使用的例子是非常简单的,但在一些更复杂的例子中,正则表达式可能更难以维护得多。
使用 w3lib.url 中的函数来从 URL 中抓取数据
有时候,你感兴趣的数据段并不单独在一个 HTML 标签内。通常,你需要从页面上列出的 URL 中获取一些参数的值。例如,你可能对获取在 HTML 中列出的 URL 中的‘username’的值感兴趣:
<div class="users">
<ul>
<li><a href="/users?username=johndoe23">John Doe</li>
<li><a href="/users?active=0&username=the_jan">Jan Roe</li>
…
<li><a href="/users?active=1&username=janie&ref=b1946ac9249&gas=_ga=1.234.567">Janie Doe</li>
</ul>
</div>
也许你会试图使用 正则表达式的超能力 , 但是,请淡定,这里的 w3lib 有一个更可靠的解决方案可以挽救局面:
>>> from w3lib.url import url_query_parameter
>>> url_query_parameter('/users?active=0&username=the_jan', 'username')
'the_jan'
假如你对 w3lib 感到陌生,那么看一看 文档 。稍后,我们将在我们的“跟着高手学习 Scrapy 技巧”系列中覆盖此 Python 库的一些其他功能。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论