scrapy提取wikipedia实践1

1.新建Project

首先创建一个新的project,在cmd下执行以下命令

scrapy startproject wiki
得到一个新的project

2.新建spider文件

现在新建一个spider来抓取wikipedia英文主页上的内容。使用以下命令新建一个spider文件

scrapy genspider main en.wikipedia.org
然后在编译器里打开在spiders下生成的main.py文件,可见代码如下

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

class MainSpider(scrapy.Spider):
    name = "main"
    allowed_domains = ["en.wikipedia.org"]
    start_urls = (
        'http://www.en.wikipedia.org/',
    )

    def parse(self, response):
        pass
然后把start_urls中的url改成wikipedia主页的url:
http://en.wikipedia.org/wiki/Main_Page


3.提取栏目标题

下面我想做的第一步是把其主页中的主要栏目的题目提取出来,例如
"From today's featured articles", "In the news", "Did you know" 之类的,共有九个。
查看html源码发现它们都是h2 tag,所以只要提取出所有的h2 tags,肯定能把这些题目包括在内。
其每一个题目的html源码都如下,

​From today's featured article​

所以在写xpath路径时只要找到 h2 下的 span 的 text就可以了。

在浏览器中,用xpath查看的代码和结果如下
$x('//h2/span/text()')
[
"From today's featured article"
, 
"Did you know..."
, 
"In the news"
, 
"On this day..."
, 
"From today's featured list"
, 
"Today's featured picture"
, 
"Other areas of Wikipedia"
, 
"Wikipedia's sister projects"
, 
"Wikipedia languages"
]


进一步,我们可以在scrapy shell 中执行得到同样的结果。命令如下:

scrapy shell https://en.wikipedia.org/wiki/Main_Page

response.xpath('//h2/span/text()').extract()
这样可以得到和上述一样的结果。
如果觉得cmd中的各种log很烦人的话可以加一个 --nolog
scrapy crawl main --nolog
这样做之后输出结果会清爽很多。

4.Project中编写

现在尝试把这些命令的功能写入文件中。
现在做一个非常简单的spider,功能就是把wikipedia英文主页上所有版块的标题,以及所有超链接的url全部打印出来

首先改写 items.py 文件,item相当于字典,但是更稳定功能更多一些(具体怎么回事现在我还不清楚)
不过可以把抓取下来的内容存放在自定的item对象中,虽然我也还不知道该怎么用这个对象。

items.py

from scrapy.item import Item, Field

class WikiItem(Item):
    titles = Field()
    urls = Field()
在这里定义了两个域,titles和urls,分别用于存放题目和链接地址

main.py

from scrapy.loader import ItemLoader
from wiki.items import WikiItem
import scrapy


class MainSpider(scrapy.Spider):
    name = "main"
    allowed_domains = ["en.wikipedia.org"]
    start_urls = (
        'http://en.wikipedia.org/wiki/Main_Page',
    )

    def parse(self, response):
        l = ItemLoader(item=WikiItem, response=response)
        l.add_xpath('titles', '//h2/span/text()')  # get all block title
        l.add_xpath('urls', '//*[@href]/@href')  # get all hyperlinks
        print response.xpath('//h2/span/text()').extract()
        print response.xpath('//*[@href]/@href').extract()
        return l.load_item()
这里规定了这个spider抓取数据的目标网址,spider名称。
在parse方法里,用一个ItemLoader把用xpath规则提取的数据导入一个WikiItem,就是在item.py中定义的那个,最后返回这个item.
并且再把题目和地址都打印出来。我认为显然是有方法能只抓取一次数据然后既存储又打印的,不过现在还不知道。

在这个project目录下运行cmd,并执行以下命令:

scrapy crawl main --nolog

就能得到想要的结果,两个list,第一个很小,存放九个题目。第二个有373个元素,存放373个url。


5. 导出数据

先尝试在此项目目录下的 scrapy shell 中进行操作,依次执行以下命令:

scrapy shell https://en.wikipedia.org/wiki/Main_Page
from scrapy.loader import ItemLoader
from wiki.items import WikiItem
import scrapy
import json

l = ItemLoader(item=WikiItem, response=response)
l.add_xpath('titles', '//h2/span/text()')
l.add_xpath('urls', '//*[@href]/@href')

(其实这就是简单复制了上述文件中的代码)

然后尝试生成一个 Item 存放 l 这个 ItemLoader 中的数据。所以我尝试了下面这行命令:

dataItem = l.load_item()
但是结果报错了,内容如下:

TypeError: 'ItemMeta' object does not support item assignment

但是既然parse中的return没有报错,是不是写一个函数return一下就能赋值了?

运行如下命令:

def loaditem(loader):
    return loader.load_item()

dataItem = loaditem(l)
但是结果还是会报同样的错。看来 ItemLoader 这个类不支持直接分配新的 item。到此一个不太明白的问题是既然不能直接赋值,parse() 方法中的 return l.load_item() 也没有报错,那这个返回值到底有什么作用?

不过既然这样,那么只能声明一个WikiItem对象了。

dataItem = WikiItem()
dataItem['titles'] = response.xpath('//h2/span/text()').extract()
dataItem['urls'] = response.xpath('//*[@href]/@href').extract()
dataDict = dict(dataItem)
dataJson = json.dumps()
通过查看datajson的类型发现这是一个str,所以说相当于以json的格式encode之后用str进行存储。

用json.loads()方法就能得到原始数据:

origin = json.loads(dataJson)

下面把json格式数据存储到txt中:

file = open('data.txt', 'wb')
file.write(dataJson)
file.close()
存储到.json文件中:

fjs = open('data.json', 'wb')
fjs.write(dataJson)
fjs.close()

不过根据Scrapy的文档,我发现可以通过project中的pipeline来解决数据导出的问题


5.1 Item Pipeline

根据scrapy文档照样子改写一下pipeline.py:
# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html

import json

class WikiPipeline(object):
    def process_item(self, item, spider):
        return item


class JsonWriterPipeline(object):
    def __init__(self):
        self.file = open('data.json', 'wb')

    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + "\n"
        self.file.write(line)
        return item
其实是在文件里增加了json的导入和一个新类:JsonWriterPipeline
然后按注释的要求,在setting.py中把新的用于导出json的类写入,所以把setting.py中负责pipelines的一块改成下面这样:
# Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    'wiki.pipelines.JsonWriterPipeline': 300,
}
然后在shell中运行一下
scrapy crawl main
果然在project目录下出现了一个名为 data.json 的文件。但是文件中并无任何内容。

那如果把spider中的item loader该问简单的声明item呢?
改后的main.py
# -*- coding: utf-8 -*-

#from scrapy.loader import ItemLoader
from wiki.items import WikiItem
import scrapy
#import json


class MainSpider(scrapy.Spider):
    name = "main"
    allowed_domains = ["en.wikipedia.org"]
    start_urls = (
        'http://en.wikipedia.org/wiki/Main_Page',
    )

    def parse(self, response):
        """
        l = ItemLoader(item=WikiItem, response=response)
        l.add_xpath('titles', '//h2/span/text()')  # get all block title
        l.add_xpath('urls', '//*[@href]/@href')  # get all hyperlinks
        print response.xpath('//h2/span/text()').extract()
        print response.xpath('//*[@href]/@href').extract()
        return l.load_item()
        """
        item = WikiItem()
        item['titles'] = response.xpath('//h2/span/text()').extract()
        item['urls'] = response.xpath('//*[@href]/@href').extract()
        print(item)
        return item
然后再运行一遍crawl的命令:
scrapy crawl main

果然,此时目录下的 data.json 文件中正常记录了抓取的数据。
















你可能感兴趣的:(Python,Scrapy)