Scrapy转换从Unicode到UTF-8

5

我编写了一个简单的脚本来从某个网站提取数据。脚本按预期工作,但我对输出格式不满意。
这是我的代码:

class ArticleSpider(Spider):
    name = "article"
    allowed_domains = ["example.com"]
    start_urls = (
        "http://example.com/tag/1/page/1"
    )

    def parse(self, response):
        next_selector = response.xpath('//a[@class="next"]/@href')
        url = next_selector[1].extract()
        # url is like "tag/1/page/2"
        yield Request(urlparse.urljoin("http://example.com", url))

        item_selector = response.xpath('//h3/a/@href')
        for url in item_selector.extract():
            yield Request(urlparse.urljoin("http://example.com", url),
                      callback=self.parse_article)

    def parse_article(self, response):
        item = ItemLoader(item=Article(), response=response)
        # here i extract title of every article
        item.add_xpath('title', '//h1[@class="title"]/text()')
        return item.load_item()

我对输出结果不满意,类似这样:

[scrapy] DEBUG: Scraped from <200 http://example.com/tag/1/article_name> {'title': [u'\xa0"\u0412\u041e\u041e\u0411\u0429\u0415-\u0422\u041e \u0421\u0412\u041e\u0411\u041e\u0414\u0410 \u0417\u0410\u041a\u0410\u041d\u0427\u0418\u0412\u0410\u0415\u0422\u0421\u042f"']}

我认为我需要使用自定义的ItemLoader类,但我不知道如何操作。需要您的帮助。

简短版: 我需要将Scrapy爬取的unicode文本转换为utf-8


1
这只是Scrapy打印Unicode字符(西里尔字母)。你是如何保存你抓取的数据项的?一旦保存了它,你会怎么处理呢?Unicode问题通常取决于您用来查看Unicode数据的软件。 - Steve
稍后我将把它保存到postgresql数据库(使用管道),但现在我正在运行scrapy crawl article -o file.json,并且我在json文件中看到相同的输出。必须承认我是Scrapy的新手,所以我欢迎任何批评) - GriMel
2个回答

7
正如您在下面看到的,这不是Scrapy的问题,而更多的是Python本身的问题。它可能在某种程度上被称为问题 :)
$ scrapy shell http://censor.net.ua/resonance/267150/voobscheto_svoboda_zakanchivaetsya

In [7]: print response.xpath('//h1/text()').extract_first()
 "ВООБЩЕ-ТО СВОБОДА ЗАКАНЧИВАЕТСЯ"

In [8]: response.xpath('//h1/text()').extract_first()
Out[8]: u'\xa0"\u0412\u041e\u041e\u0411\u0429\u0415-\u0422\u041e \u0421\u0412\u041e\u0411\u041e\u0414\u0410 \u0417\u0410\u041a\u0410\u041d\u0427\u0418\u0412\u0410\u0415\u0422\u0421\u042f"'

你所看到的是同一件事物的两种不同表示方式 - Unicode 字符串。
我建议您使用 -L INFO 运行爬虫,或者在您的 settings.py 中添加 LOG_LEVEL='INFO' 以避免在控制台中显示此输出。
一个令人烦恼的问题是,当您保存为 JSON 时,您会得到转义的 Unicode JSON,例如:
$ scrapy crawl example -L INFO -o a.jl

给你:

$ cat a.jl
{"title": "\u00a0\"\u0412\u041e\u041e\u0411\u0429\u0415-\u0422\u041e \u0421\u0412\u041e\u0411\u041e\u0414\u0410 \u0417\u0410\u041a\u0410\u041d\u0427\u0418\u0412\u0410\u0415\u0422\u0421\u042f\""}

这是正确的,但它占用更多的空间,大多数应用程序同样可以处理非转义的JSON。
在您的settings.py中添加几行代码即可更改此行为:
from scrapy.exporters import JsonLinesItemExporter
class MyJsonLinesItemExporter(JsonLinesItemExporter):
    def __init__(self, file, **kwargs):
        super(MyJsonLinesItemExporter, self).__init__(file, ensure_ascii=False, **kwargs)

FEED_EXPORTERS = {
    'jsonlines': 'myproject.settings.MyJsonLinesItemExporter',
    'jl': 'myproject.settings.MyJsonLinesItemExporter',
}

基本上我们所做的就是为默认的JSON项目导出器设置ensure_ascii=False。这可以防止转义。我希望有一种更简单的方式来传递参数给导出器,但我看不到任何方法,因为它们在此处周围使用其默认参数进行初始化。无论如何,现在你的JSON文件包含:
$ cat a.jl
{"title": " \"ВООБЩЕ-ТО СВОБОДА ЗАКАНЧИВАЕТСЯ\""}

更美观,同样有效且更紧凑。

0

有两个独立的问题影响Unicode字符串的显示。

  1. 如果你返回一个字符串列表,输出文件将会有一些问题,因为它默认使用ascii编码来序列化列表元素。你可以像下面这样解决它们,但更合适的是按照@neverlastn建议使用extract_first()

    class Article(Item):
        title = Field(serializer=lambda x: u', '.join(x))
    
  2. repr()方法的默认实现会将unicode字符串序列化为它们的转义版本\uxxxx。你可以通过在item类中覆盖此方法来改变这种行为

    class Article(Item):
        def __repr__(self):
            data = self.copy()
            for k in data.keys():
                if type(data[k]) is unicode:
                    data[k] = data[k].encode('utf-8')
            return super.__repr__(data)
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接