【问题标题】:Scrapy - Scraping Websites With 'Less Than' Signs In TextScrapy - 在文本中使用“小于”符号抓取网站
【发布时间】:2018-05-30 04:00:04
【问题描述】:

更新:

这是 html 的示例行,直接在 Chrome 中使用“复制外部 html”进行复制。我在 td 和 /td 之前添加了空格以显示实际的 html 而不会在这篇文章中触发 html:

<td class="elem">3Fb1&lt;+1Lo&lt;+3Sb1</td> 

使用scrapy shell,我运行这个命令:

response.xpath('//table[@class="elm"][1]//td[@class="elem"]//text()')

来自响应的数据是:

3Fb1

但应该是的

3Fb1<+1Lo<+3Sb1

我相信选择器在第一个刻度('

非常感谢任何帮助。


我是 scrapy 的新手,我一直在从事一个项目(个人项目,为我的孩子)收集大量与花样滑冰得分相关的统计数据。得分统计广泛使用“

分数以表格的形式报告,“ele”的表格类和根据位置编号的表格,然后包含执行的滑冰元素和行中的分数。

一个示例评分条目(表格中的单元格)可以是:2A&lt;

底层代码为:&lt;td class="elem"&gt;2A&amp;lt;&lt;/td&gt;

或者这个:2A+1Lo&lt;+2F

底层编码如下:

<td class="elem">2A+1Lo&lt;+2F</td>

我已经定义了对象(可能不是正确的术语)行来迭代,然后使用它来获取特定的单元格(第二个单元格始终是执行的元素):

elements['executed_element'] = row.xpath('td[2]//text()').extract()

当刻度出现在文本末尾时(如第一个示例),我得到了所有内容,但当它位于文本中间时(第二个示例),它会截断它之后的所有内容。

我会回去手动修复,但我要提取几百万个数据点,所以这样做不切实际。

对这个新手的任何帮助将不胜感激。

抓取示例页面:http://www.usfigureskating.org/leaderboard/results/2018/25073/SEGM001.html

代码:

def parse(self, response):
    event = response.xpath('//title//text()').extract()
    category_segment = response.xpath('//h2[@class="catseg"]//text()').extract()
    skater_number = 1
    for row in response.xpath('//table[@class="sum"]/tbody/tr[not(contains(@class,"thead"))]'):
        skater_name = row.xpath('td[2]//text()').extract_first()
        skater_place = row.xpath('td[1]//text()').extract_first()
        skater_deductions = row.xpath('td[7]//text()').extract_first()
        # capture elements detail 
        skater_table = skater_place
        elements_id = 1
        element_table = '//table[@class="elm"][' + str(skater_table) +']/tbody/tr[not(contains(@class,"thead"))]'
        for row in response.xpath(element_table):
            elements = {}
            elements['Event'] = event 
            elements['Category_Segment'] = category_segment
            elements['skater_name'] = skater_name 
            elements['elements_id'] = elements_id
            elements['element_number'] = row.xpath('td[@class="num"]//text()').extract()
            elements['executed_element'] = row.xpath('td[2]//text()').extract()
            elements['element_info'] = row.xpath('td[3]//text()').extract()
            elements['base_value'] = row.xpath('td[4]//text()').extract()
            elements['bonus'] = row.xpath('td[5]//text()').extract()
            elements['GOE'] = row.xpath('td[6]//text()').extract()
            goe_table = str('.//td[@class="jud"]')
            judge_pointer = 8
            judge_number = 1
            elements_id += 1
            for cell in row.xpath(goe_table):
                elements['Judge Number'] = judge_number
                elements['Judge_GOE_Score'] = row.xpath('td[' + str(judge_pointer) + ']//text()').extract()
                yield elements
                judge_pointer += 1
                judge_number += 1

【问题讨论】:

  • 最好显示此页面的网址和您的代码,以便我们对其进行测试。
  • 我的代码部分(尽可能多)在 response.xpath('//table[@class="sum"]/tbody/tr[not(contains(@ class,"thead"))]'): elements_id = 1 element_table = '//table[@class="elm"][' + str(skater_table) +']/tbody/tr[not(contains(@class, "thead"))]' for row in response.xpath(element_table): elements = {} elements['element_number'] = row.xpath('td[@class="num"]//text()')。 extract() elements['executed_element'] = row.xpath('td[2]//text()').extract()
  • 输入有问题的代码。它会更具可读性。
  • 和网址,您也可以添加到问题中。您应该在创建问题时添加 url 和代码。

标签: python html xpath scrapy html-parsing


【解决方案1】:

这不是一个乱七八糟的问题,而是一个lxml 的问题。在这种情况下,您仍然可以使用 scrapy,但使用不同的解析器:

>> from scrapy import Selector

>> sel = Selector(text=response.body, type="xml")
>> sel.xpath('//table[@class="elm"][1]//td[@class="elem"]//text()') # should return it correctly

您必须使用sel 而不是response 从该页面提取信息。

已知问题已经是reported here

【讨论】:

    【解决方案2】:

    我使用程序 wget 下载了您的页面,并在文本编辑器中检查了它 - 它不使用 &amp;lt; 代替 &lt; 所以 scrapy 有问题 - 但仅适用于 &lt;&lt;+&lt;+

    我将&lt;&lt;+ 替换为&amp;lt&amp;lt+,将&lt;+ 替换为&amp;lt+

    body = response.body.replace(b'<<+', b'&lt;&lt;+').replace(b'<+', b'&lt;+')
    

    然后我创建选择器

    selector = scrapy.Selector(text=body.decode('utf-8'))
    

    我可以与 css() 一起使用,它给了我正确的结果

    #!/usr/bin/env python3
    
    import scrapy
    
    class MySpider(scrapy.Spider):
    
        name = 'myspider'
    
        start_urls = ['http://www.usfigureskating.org/leaderboard/results/2018/25073/SEGM001.html']
    
        def parse(self, response):
            print('url:', response.url)
    
            body = response.body.replace(b'<<+', b'&lt;&lt;+').replace(b'<+', b'&lt;+')
    
            selector = scrapy.Selector(text=body.decode('utf-8'))
    
            i = 1
            for x  in selector.css('.elem::text').extract():
                if 'Elements' in x:
                    print('---', i, '---')
                    i += 1
                else:
                    print(x)
    
    # --- it runs without project and saves in `output.csv` ---
    
    from scrapy.crawler import CrawlerProcess
    
    c = CrawlerProcess({
        'USER_AGENT': 'Mozilla/5.0',
    
        # save in CSV or JSON
        #'FEED_FORMAT': 'csv',     # 'json
        #'FEED_URI': 'output.csv', # 'output.json
    })
    c.crawl(MySpider)
    c.start()
    

    结果:

    Executed
    --- 1 ---
    2Ab1+2T
    ChSt1
    2Ab1
    2Lz+1Lo+2Lo
    2Lz
    FSSp4
    2F
    CCoSp4
    Executed
    --- 2 ---
    2Ab1
    ChSt1
    2Ab1+1Lo+2F
    CCoSp2V
    2Lz+2Lo
    2Lo
    2Lz
    LSp4
    Executed
    --- 3 ---
    CCoSp4
    ChSt1
    2Ab1+2Lo
    2Lz+1Lo+2Lo
    2Ab1
    2Lz
    2Fe
    FSSp4
    Executed
    --- 4 ---
    2Ab1+1Lo+2Lo
    2Ab1
    LSp4
    ChSt1
    2Lz
    2F
    2Lz+2T
    CCoSp4
    Executed
    --- 5 ---
    2Ab1
    LSp2
    ChSt1
    2Ab1+1Lo+1Lo
    2Lz+2Lo
    2Lz
    2F
    CCoSp3
    Executed
    --- 6 ---
    2Lz
    1A
    SSp3
    ChSt1
    2Lz+1Lo+2Lo
    CCoSp3
    2F+2Lo
    2F
    Executed
    --- 7 ---
    2F
    2Ab1
    CCoSp4
    2Lz
    2Ab1<+2T
    ChSt1
    2Lz+1Lo+2F
    LSp4
    Executed
    --- 8 ---
    1A
    LSp4
    ChSt1
    2Lz
    2Lz+2T
    2Lo+2T+1Lo
    2F
    CCoSp4
    Executed
    --- 9 ---
    2A<<
    CCoSp4
    ChSt1
    2F+1Lo+2Lo
    2Lze+2Lo
    2Lze
    2F
    SSp4
    Executed
    --- 10 ---
    2Lz
    2Ab1
    SSp3
    ChSt1
    2A<<+REP
    2Lz+2Lo
    2F
    CCoSp4
    Executed
    --- 11 ---
    FSSp4
    2Ab1<+2Lo
    ChSt1
    2A<<
    FCCoSp3
    2F+2Lo<+1Lo<<
    2Lz
    2F
    Executed
    --- 12 ---
    2A<<+1Lo+2Lo<
    2Lze
    SSp3
    ChSt1
    2A<<
    2F
    2F+2Lo<
    CCoSp3
    

    【讨论】:

      【解决方案3】:

      您遇到的问题是由于保留字符(小于号 &amp;lt;)而不是 &amp;lt; 而导致的 HTML 格式错误。

      一种在response 上使用带有html5lib 解析器后端的BeautifulSoup 的解决方法,就像这样(如this answer 中所建议的那样)。通过用解析的内容覆盖您的响应正文,您应该能够使用您当前的代码:

      from bs4 import BeautifulSoup
      from scrapy.http import TextResponse
      
      # parse response body with BeautifulSoup
      soup = BeautifulSoup(response.body, "html5lib")
      # overwrite response body
      response = TextResponse(url="my HTML string", body=str(soup))
      
      # from here on use your code
      event = response.xpath('//title//text()').extract()
      ...
      

      希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 2013-05-09
        • 2020-10-12
        • 1970-01-01
        • 1970-01-01
        • 2014-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多