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源码都如下,
<h2 id="mp-tfa-h2" style="margin:3px; background:#cef2e0; font-family:inherit; font-size:120%; font-weight:bold; border:1px solid #a3bfb1; text-align:left; color:#000; padding:0.2em 0.4em;">
<span class="mw-headline" id="From_today.27s_featured_article">From today's featured article</span>
</h2>
所以在写xpath路径时只要找到 h2 下的 span 的 text就可以了。
在浏览器中,用xpath查看的代码和结果如下
进一步,我们可以在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()
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
果然,此时目录下的 data.json 文件中正常记录了抓取的数据。
# -*- 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