检查文本是否存在大量关键字

时间:2015-08-09 00:27:20

标签: python html xpath web-scraping scrapy

假设我想检查网页是否存在任意数量的关键字。我该怎么做呢?

我测试了xpath选择器if response.xpath('//*[text()[contains(.,"red") or contains(.,"blue") or contains(.,”green”)]]'):,它按预期工作。如上所述,我有兴趣检查的实际关键字集太大而不能方便地手动输入。我感兴趣的是一种通过基于填充关键词的文件内容生成选择器来自动化该过程的方法。

从包含每个关键字的文本文件开始,我如何打开该文件并使用它来检查它包含的关键字是否出现在给定xpath的文本元素中?

我使用线程Xpath contains value A or value BXPATH Multiple Element Filters来提出我的手动输入解决方案,但还没有找到任何可以解决自动化的问题。

澄清

我只是检查一下给定的xpath是否包含我列表中提供的任何关键字。我还想使用它们的存在作为从页面中抓取内容的前提条件。我测试的手动系统的工作原理如下:

item_info = ItemLoader(item=info_categories(), response=response)
if response.xpath('//*[text()[contains(.,"red") or contains(.,"blue") or contains(.,”green”)]]'):
    item_info.add_xpath('title', './/some/x/path/text()')
    item_info.add_xpath('description', './/some/other/x/path/text()')
return item_info.load_item()

虽然@ alecxe的解决方案允许我根据关键字设置检查网页文字,但是从' print'切换到'如果'并试图控制我提取的信息返回SyntaxError: invalid syntax。我可以将列表中关键字的阅读方便性与手动输入的功能结合起来吗?

更新 - 探索Frederic Bazin的正则表达式解决方案

在过去的几天里,我一直在使用正则表达式来限制我的解析。我的代码采用弗雷德里克的提案,经过一些修改以解决错误,如下所示:

item_info = ItemLoader(item=info_categories(), response=response)
keywords = '|'.join(re.escape(word.strip()) for word in open('keys.txt'))
r = re.compile('.*(%s).*' % keywords, re.MULTILINE|re.UNICODE)
if r.match(response.body_as_unicode()):
    item_info.add_xpath('title', './/some/x/path/text()')
    item_info.add_xpath('description', './/some/other/x/path/text()')
return item_info.load_item()

此代码运行时没有错误,但Scrapy报告已抓取0个项目并抓取0个项目,因此显然出现了问题。

我试图通过从Scrapy shell运行它来进行调试。我的结果表明,keywordsr步骤都表现出来。如果我使用上面的方法为包含红色,蓝色和绿色字样的.txt文件定义并调用keywords,我会收到回复'red|blue|green'。如上所述定义和调用r会给我<_sre.SRE_Pattern object at 0x17bc980>,我认为这是预期的响应。但是,当我运行r.match(response.body_as_unicode())时,即使在我知道包含一个或多个关键字的网页上,我也没有收到回复。

有没有人想过我在这里失踪了什么?据我所知,只要我的一个关键字出现在response.body中,就应该触发一个匹配,并且Scrapy应该继续使用我定义的xpath从该响应中提取信息。显然我错了,但我不确定如何或为什么。

解决方案

我想我最后可能会发现这个问题。我目前的结论是,在r.match上执行response.body_as_unicode会导致难度。提供的文档here表示匹配:

  

如果字符串开头的零个或多个字符与正则表达式模式匹配,则返回相应的MatchObject实例。如果字符串与模式不匹配,则返回None;请注意,这与零长度匹配不同。

     

请注意,即使在MULTILINE模式下,re.match()也只会匹配字符串的开头而不是每行的开头。

这种行为不适合我的情况。我有兴趣从包含我的关键字任何地方的网页中识别和抓取信息,而不是那些将我的某个关键字作为页面上的第一个项目。为了完成该任务,我需要re.search,它扫描一个字符串,直到它找到compile生成的正则表达式模式匹配并返回MatchObject,否则返回None当模式不匹配时。

我现在的(工作!)代码如下。请注意,除了从match切换到search之外,我还添加了一些关键字,以限制与整个单词的匹配。

item_info = ItemLoader(item=info_categories(), response=response)
keywords = '|'.join(r"\b" + re.escape(word.strip()) + r"\b" for word in open('keys.txt'))
r = re.compile('.*(%s).*' % keywords, re.MULTILINE|re.UNICODE)
if r.search(response.body_as_unicode()):
    item_info.add_xpath('title', './/some/x/path/text()')
    item_info.add_xpath('description', './/some/other/x/path/text()')
return item_info.load_item()

2 个答案:

答案 0 :(得分:1)

正则表达式可能是在大量页面上运行测试的最快方法

import re
keywords = '|'.join(re.escape(word.strip()) for word in open('keywords.txt'))
r = re.compile('.*(%s).*' % keywords, re.MULTILINE|re.UNICODE)
if r.match(response.body_as_unicode()):

在多个关键字上生成xpath表达式可以工作,但是在运行XPATH之前添加额外的CPU负载(通常约100ms)将页面解析为XML。

答案 1 :(得分:0)

您还可以检查关键字是否在response.body

source = response.body
with open('input.txt') as f:
    for word in f:
        print word, word.strip() in source

或者,使用any()

with open('input.txt') as f:
    print any(word.strip() in source for word in f)