Scrapy - 在文本中使用“少于”标记的网站

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scrapy - 在文本中使用“少于”标记的网站相关的知识,希望对你有一定的参考价值。

更新:

这是html的示例行,直接使用Chrome中的“copy outer 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

我相信选择器在第一个滴答符('<')处停止,因为它似乎是新html标记的开头(或者,我从技术上讲,它是td标记文本的结尾)。仔细观察,它确实显示编码使用<而不是'<'字符。我希望有一个简单的限定符,我可以添加到我的xpath请求中忽略这些,但是在一周后(在我几个小时内)谷歌搜索和阅读,我找不到任何东西。

任何帮助是极大的赞赏。


我是scrapy的新手,我一直致力于一个项目(个人,我的孩子)收集大量与花样滑冰得分相关的统计数据。得分统计广泛使用'<'和'<<'来表示得分细节(滑手称这些'滴答',我将用它来引用它们)。

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

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

其底层代码为:<td class="elem">2A&lt;</td>

或者这个:2A+1Lo<+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
答案

我下载了你的页面,程序wgetand在文本编辑器中检查它 - 它没有使用&lt;用于<所以scrapy有问题 - 但只有<<+<+

我用<<+&lt&lt+<+取代了&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
另一答案

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

一个解决方法是在你的html5lib上使用BeautifulSoup和response解析器后端(如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()
...

希望这可以帮助!

另一答案

这不是一个scrapy问题,而是一个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

以上是关于Scrapy - 在文本中使用“少于”标记的网站的主要内容,如果未能解决你的问题,请参考以下文章

在 Scrapy 中使用 Xpath 选择段落下方的任何文本

scrapy+Lucene搭建小型搜索引擎

Scrapy 忽略了部分文本

使用 Scrapy 抓取 Python 数据

scrapy_随机ip代理池

使用 Scrapy 在 Python 中选择部分文本字段