自动检测来自用户给定项目的 Xpath 作为输入
Automatically detect Xpath from user given item as input
我正在编写一个 Flask 应用程序,它运行 Scrapy 蜘蛛来抓取给定的网站。 Flask 应用程序提供了一个 UI,它接受要报废的 URL 和一个要报废的 ITEM(比如 website 的职位)。
现在的问题是,蜘蛛如何自动检测用户给定 ITEM 的 Xpath ?????
以下是 spider 的代码(带有硬编码的 Xpath):
class StackItem(scrapy.Item):
def __setitem__(self, key, value):
if key not in self.fields:
self.fields[key] = scrapy.Field()
self._values[key] = value
class newAppSpider(CrawlSpider):
name = "appSpider"
def __init__(self, *args, **kwargs):
super(newAppSpider, self).__init__(*args, **kwargs)
self.start_urls = [kwargs.get('start_url')]
rules = (Rule (SgmlLinkExtractor(allow=('.*\?id1=.*',),restrict_xpaths=('//a[@class="prevNext next"]',))
, callback="parse_items", follow= True),)
def parse_items(self, response):
hxs = HtmlXPathSelector(response)
posts = hxs.select("//article[@class='classified']")
items = []
for post in posts:
item = StackItem()
item[self.settings['MY_PROPERTY']] = post.select("div[@class='uu mb2px']/a/strong/text()").extract()
items.append(item)
for item in items:
yield item
因为您要在节点集中查找文本(每个作业 posting),所以您必须遍历 posting 的节点并查看该节点是否包含您的文本需要。
如果你想让它变得非常通用,这是 XPath 的一个大问题。
但是如果特定 post 包含此文本,您可以使用如下内容进行过滤:
for post in posts:
text_node = self.find_text(post, "Job Title")
if text_node:
print text_node.xpath('text()').extract()
以及进入蜘蛛的 find_text
函数(这可以进一步改进):
def find_text(self, node, text):
for child in node.xpath("./child::node()"):
if child.xpath("./text()[contains(.,'{0}')]".format(text)).extract():
return child
if child.xpath(".//text()[contains(.,'{0}')]".format(text)).extract():
return self.find_text(child, text)
注意 XPath 的 contains
区分大小写。这意味着 职位 不等于 职位。
但是,如果您查看该站点,您会发现 "Job Title" 会产生一个小问题,因为该标签包含的不仅仅是职位名称。
还有一件事
因为您使用了规则,所以您的 start_url
没有被解析。如果您输入 "Job Title" 作为搜索文本,这是显而易见的,它会在更深的地方找到包含此文本的职位描述。要解决此问题,您必须覆盖蜘蛛的 parse_start_url
函数:
def parse_start_url(self, response):
self.parse_items(response)
对于启动错误
为您从 Flask 网站获得的价值加上引号:
command = "scrapy crawl appSpider -a start_url=" + request.form['url'] + '-s MY_PROPERTY="' + request.form['tag1']+'"'
我正在编写一个 Flask 应用程序,它运行 Scrapy 蜘蛛来抓取给定的网站。 Flask 应用程序提供了一个 UI,它接受要报废的 URL 和一个要报废的 ITEM(比如 website 的职位)。
现在的问题是,蜘蛛如何自动检测用户给定 ITEM 的 Xpath ?????
以下是 spider 的代码(带有硬编码的 Xpath):
class StackItem(scrapy.Item):
def __setitem__(self, key, value):
if key not in self.fields:
self.fields[key] = scrapy.Field()
self._values[key] = value
class newAppSpider(CrawlSpider):
name = "appSpider"
def __init__(self, *args, **kwargs):
super(newAppSpider, self).__init__(*args, **kwargs)
self.start_urls = [kwargs.get('start_url')]
rules = (Rule (SgmlLinkExtractor(allow=('.*\?id1=.*',),restrict_xpaths=('//a[@class="prevNext next"]',))
, callback="parse_items", follow= True),)
def parse_items(self, response):
hxs = HtmlXPathSelector(response)
posts = hxs.select("//article[@class='classified']")
items = []
for post in posts:
item = StackItem()
item[self.settings['MY_PROPERTY']] = post.select("div[@class='uu mb2px']/a/strong/text()").extract()
items.append(item)
for item in items:
yield item
因为您要在节点集中查找文本(每个作业 posting),所以您必须遍历 posting 的节点并查看该节点是否包含您的文本需要。
如果你想让它变得非常通用,这是 XPath 的一个大问题。
但是如果特定 post 包含此文本,您可以使用如下内容进行过滤:
for post in posts:
text_node = self.find_text(post, "Job Title")
if text_node:
print text_node.xpath('text()').extract()
以及进入蜘蛛的 find_text
函数(这可以进一步改进):
def find_text(self, node, text):
for child in node.xpath("./child::node()"):
if child.xpath("./text()[contains(.,'{0}')]".format(text)).extract():
return child
if child.xpath(".//text()[contains(.,'{0}')]".format(text)).extract():
return self.find_text(child, text)
注意 XPath 的 contains
区分大小写。这意味着 职位 不等于 职位。
但是,如果您查看该站点,您会发现 "Job Title" 会产生一个小问题,因为该标签包含的不仅仅是职位名称。
还有一件事
因为您使用了规则,所以您的 start_url
没有被解析。如果您输入 "Job Title" 作为搜索文本,这是显而易见的,它会在更深的地方找到包含此文本的职位描述。要解决此问题,您必须覆盖蜘蛛的 parse_start_url
函数:
def parse_start_url(self, response):
self.parse_items(response)
对于启动错误
为您从 Flask 网站获得的价值加上引号:
command = "scrapy crawl appSpider -a start_url=" + request.form['url'] + '-s MY_PROPERTY="' + request.form['tag1']+'"'