Python 是否可以找到具有相同dom结构的节点

Python 是否可以找到具有相同dom结构的节点,python,scrapy,semantics,semantic-markup,Python,Scrapy,Semantics,Semantic Markup,我已经用Scrapy从很多网站上抓取了很多HTML(内容类似),而dom结构则不同 例如,其中一个站点使用以下结构: <div class="post"> <section class='content'> Content1 </section> <section class="panel"> </section> </div> <div class="post">

我已经用Scrapy从很多网站上抓取了很多HTML(内容类似),而dom结构则不同

例如,其中一个站点使用以下结构:

<div class="post">
    <section class='content'>
        Content1
    </section>

    <section class="panel">
    </section>
</div>
<div class="post">
    <section class='content'>
        Conent2
    </section>

    <section class="panel">
    </section>
</div>
我想提取数据
Content3
Content4

而最简单的解决方案是为所有站点逐个标记所需的数据xpath。那将是一项乏味的工作

所以我想知道是否可以自动提取结构。事实上,我只需要定位到重复根节点(
div.post
article.entry
,在上面的示例中),然后我就可以使用某些规则提取数据

这可能吗


顺便说一句,我不确定这种算法的名称,所以这篇文章的标签可能是错误的,如果是真的,请随意修改

您必须至少了解一些常见模式,才能制定确定性提取规则。下面的解决方案非常原始,决不是最优的,但它可能会帮助您:

# -*- coding: utf-8 -*-
import re

import bs4
from bs4 import element
import scrapy


class ExampleSpider(scrapy.Spider):
    name = "example"
    start_urls = ['http://quotes.toscrape.com/']

    def parse(self, response):
        min_occurs = 5
        max_occurs = 1000
        min_depth = 7
        max_depth = 7
        pattern = re.compile('^/html/body/.*/(span|div)$')
        extract_content = lambda e: e.css('::text').extract_first()
        #extract_content = lambda e: ' '.join(e.css('*::text').extract())

        doc = bs4.BeautifulSoup(response.body, 'html.parser')

        paths = {}
        self._walk(doc, '', paths)
        paths = self._filter(paths, pattern, min_depth, max_depth,
                             min_occurs, max_occurs)

        for path in paths.keys():
            for e in response.xpath(path):
                yield {'content': extract_content(e)}

    def _walk(self, doc, parent, paths):
        for tag in doc.children:
            if isinstance(tag, element.Tag):
                path = parent + '/' + tag.name
                paths[path] = paths.get(path, 0) + 1
                self._walk(tag, path, paths)

    def _filter(self, paths, pattern, min_depth, max_depth, min_occurs, max_occurs):
        return dict((path, count) for path, count in paths.items()
                        if pattern.match(path) and
                                min_depth <= path.count('/') <= max_depth and
                                min_occurs <= count <= max_occurs)
#-*-编码:utf-8-*-
进口稀土
进口bs4
从bs4导入元素
进口羊瘙痒
类示例Spider(scrapy.Spider):
name=“示例”
起始URL=['http://quotes.toscrape.com/']
def解析(自我,响应):
最小值=5
最大发生次数=1000
最小深度=7
最大深度=7
pattern=re.compile(“^/html/body/*/(span | div)$”)
extract_content=lambda e:e.css('::text')。extract_first()
#extract_content=lambda e:''.join(e.css('*::text').extract())
doc=bs4.BeautifulSoup(response.body'html.parser')
路径={}
自行行走(文件、路径)
路径=自过滤器(路径、模式、最小深度、最大深度、,
最小值发生,最大值发生)
对于path.keys()中的路径:
对于响应中的e.xpath(路径):
产生{'content':提取{u content(e)}
def_walk(自我、文档、父级、路径):
对于doc.children中的标记:
如果isinstance(标记,element.tag):
path=parent+'/'+tag.name
路径[path]=路径。获取(路径,0)+1
自走(标记、路径、路径)
定义过滤器(自身、路径、图案、最小深度、最大深度、最小发生、最大发生):
返回路径的dict((path,count),路径中的count.items()
if pattern.match(路径)和

min_depth您是在寻找一个覆盖2个特定站点或未知n个站点的解决方案吗?基于这两个示例,您的内容似乎总是在一个
中。如果这适用于您正在抓取的所有网站,您可以使用
response.xpath('//section[@class=“title”]/text()).extract()
@Granitosaurus是的,但是这些网站有相同类型的内容,但dom结构不同。@Casper:这不是事实。
secion.title
只是一个例子。另一个网站可能使用
div.summary
来提取这些信息,你需要一种系统的方法来定位它。你可以定义一个可能的XPath/css列表选择器或内容应具有某种标识,将其标识为要提取的数据。
# -*- coding: utf-8 -*-
import re

import bs4
from bs4 import element
import scrapy


class ExampleSpider(scrapy.Spider):
    name = "example"
    start_urls = ['http://quotes.toscrape.com/']

    def parse(self, response):
        min_occurs = 5
        max_occurs = 1000
        min_depth = 7
        max_depth = 7
        pattern = re.compile('^/html/body/.*/(span|div)$')
        extract_content = lambda e: e.css('::text').extract_first()
        #extract_content = lambda e: ' '.join(e.css('*::text').extract())

        doc = bs4.BeautifulSoup(response.body, 'html.parser')

        paths = {}
        self._walk(doc, '', paths)
        paths = self._filter(paths, pattern, min_depth, max_depth,
                             min_occurs, max_occurs)

        for path in paths.keys():
            for e in response.xpath(path):
                yield {'content': extract_content(e)}

    def _walk(self, doc, parent, paths):
        for tag in doc.children:
            if isinstance(tag, element.Tag):
                path = parent + '/' + tag.name
                paths[path] = paths.get(path, 0) + 1
                self._walk(tag, path, paths)

    def _filter(self, paths, pattern, min_depth, max_depth, min_occurs, max_occurs):
        return dict((path, count) for path, count in paths.items()
                        if pattern.match(path) and
                                min_depth <= path.count('/') <= max_depth and
                                min_occurs <= count <= max_occurs)