【问题标题】:Scrapy/XPath: Replace inline tags within paragraphScrapy/XPath:替换段落内的内联标签
【发布时间】:2018-06-28 16:38:32
【问题描述】:

我正在尝试使用 Scrapy 从包含内联图标和其他标签的 p 中提取和清理一些文本。特别是,我想用从图像src属性中提取的文本替换图像标签:

from scrapy.selector import Selector
text = '''
<p id="1"><b><br></b>For service <i>to </i>these stations, take the <img src="images/1.png"> to 72 St or Times Sq-42 St and transfer
    <br>to an uptown <img src="images/1.png"> or <img src="images/2.png"> <i>local</i>.
    <br>
    <br>For service <i>from </i>these stations, take the <img src="images/1.png"> or <img src="images/2.png"> to 72 St or 96 St and transfer
    <br>to a South Ferry-bound <img src="images/1.png">.
    <br><b>______________________________<br></b>
</p>
'''
sel = Selector(text=text)
# do stuff

我要找的结果是字符串:

如需前往这些车站,请乘坐 (1) 到 72 St 或 Times Sq-42 St,然后转乘到住宅区 (1) 或 (2) 当地。从这些车站乘坐 (1) 或 (2) 到 72 St 或 96 St,然后换乘前往南码头的 (1)。

我可以使用以下方法从src 中提取文本:

node.css('img').xpath('@src').re_first(r'images/(.+).png')

但我坚持如何遍历子节点并确定它是否是文本节点/如何过滤掉其他内联标签。这是我所在的位置:

description = sel.css('p#1')

def clean_html(description):
    for n in description.xpath('node()'):
        if (n.xpath('self::img')):
            yield n.xpath('@src').re_first(r'images/(.+).png')
        if (n.xpath('self::text()')):
            yield n.css('::text')

text = ''.join(clean_html(description))

【问题讨论】:

    标签: xpath scrapy


    【解决方案1】:

    在这种情况下,我认为selectors 并不是特别有用。

    尝试分两个阶段处理。

    1. 使用re.sub 将整个img 标记替换为您指定的字符串 想要。
    2. 使用 BeautifulSoup 从结果字符串中删除剩余的 HTML。

    像这样:

    from scrapy.selector import Selector
    import re
    from bs4 import BeautifulSoup
    
    # manually construct a selector for demonstration purposes
    DATA = '''
    <p id="1"><b><br></b>For service <i>to </i>these stations, take the <img src="images/1.png"> to 72 St or Times Sq-42 St and transfer
        <br>to an uptown <img src="images/1.png"> or <img src="images/2.png"> <i>local</i>.
        <br>
        <br>For service <i>from </i>these stations, take the <img src="images/1.png"> or <img src="images/2.png"> to 72 St or 96 St and transfer
        <br>to a South Ferry-bound <img src="images/1.png">.
        <br><b>______________________________<br></b>
    </p>
    '''
    sel = Selector(text=DATA)
    
    # get the raw source string to work with
    text = sel.extract()
    
    # replace image tag with text from extracted file name
    image_regex = re.compile('(<img src="images/)(.+?)(.png">)', re.MULTILINE)
    replaced = re.sub(image_regex, r'(\2)', text)
    
    # remove html and return clean text
    soup = BeautifulSoup(replaced, 'lxml')
    print(soup.get_text())
    

    结果:

    如需前往这些车站,请搭乘 (1) 至 72 St 或 Times Sq-42 St 并转移 到住宅区 (1) 或 (2) 当地。

    从这些车站乘坐 (1) 或 (2) 到 72 St 或 96 St 并转移 到南渡口 (1)。 ______________________________

    【讨论】:

      【解决方案2】:

      如果没有任何额外的外部库,我会这样做:

      1. 获取文字和图片路径:

        results = selector.xpath('.//text()|.//img/@src').extract()

      2. 删除多余的空格、新行和下划线:

        results = map(lambda x: x.strip('\n_ '), results)

      3. 删除空字符串:

        results = filter(None, results)

      4. 将结果合并成一个段落并修复点:

        raw_paragraph = " ".join(results).replace(' .', '.')

      5. images/{Number}.png 替换为({Number})

        paragraph = re.sub('images/(?P&lt;number&gt;\d+).png', '(\g&lt;number&gt;)', raw_paragraph)

      结果:For service to these stations, take the (1) to 72 St or Times Sq-42 St and transfer to an uptown (1) or (2) local. For service from these stations, take the (1) or (2) to 72 St or 96 St and transfer to a South Ferry-bound (1).

      【讨论】:

      • 感谢您的回答!我接受了@jschnurr 的回答,因为 bs4 是我最终自己解决问题的方式。另外,我觉得留在 DOM 中而不是直接作为字符串进行操作更好(这就是我在使用 scrapy 的内置选择器时苦苦挣扎的方式!)
      猜你喜欢
      • 1970-01-01
      • 2016-05-17
      • 1970-01-01
      • 2012-06-18
      • 1970-01-01
      • 2011-02-26
      • 1970-01-01
      • 1970-01-01
      • 2011-01-13
      相关资源
      最近更新 更多