Scrapy笔记02- 完整示例

这篇文章我们通过一个比拟完全的例子来教你应用Scrapy,我选择爬取虎嗅网首页的新闻列表。

这里我们将完成以下几个步骤:

  • 创立一个新的Scrapy工程
  • 定义你所须要要抽取的Item对象
  • 编写一个spider来爬取某个网站并提取出所有的Item对象
  • 编写一个Item Pipline来存储提取出来的Item对象

Scrapy应用Python语言编写,如果你对这门语言还不熟,请先去学习下根本知识。

创立Scrapy工程

在任何你爱好的目录履行以下命令

1
scrapy startproject coolscrapy

将会创立coolscrapy文件夹,其目录构造以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
coolscrapy/
scrapy.cfg # 安排配置文件

coolscrapy/ # Python模块,你所有的代码都放这里面
__init__.py

items.py # Item定义文件

pipelines.py # pipelines定义文件

settings.py # 配置文件

spiders/ # 所有爬虫spider都放这个文件夹下面
__init__.py
...

定义我们的Item

我们通过创立一个scrapy.Item类,并定义它的类型为scrapy.Field的属性,我们预备将虎嗅网新闻列表的名称、连接地址和摘要爬取下来。

1
2
3
4
5
6
7
import scrapy

class HuxiuItem(scrapy.Item):
title = scrapy.Field() # 题目
link = scrapy.Field() # 连接
desc = scrapy.Field() # 简述
posttime = scrapy.Field() # 宣布时光

或许你认为定义这个Item有点麻烦,但是定义完以后你可以得到许多利益,这样你便可以够应用Scrapy中其他有用的组件和赞助类。

第一个Spider

蜘蛛就是你定义的一些类,Scrapy应用它们来从一个domain(或domain组)爬取信息。在蜘蛛类中定义了一个初始化的URL下载列表,和怎样跟踪连接,如何解析页面内容来提取Item。

定义一个Spider,只需继承scrapy.Spider类并定于一些属性:

  • name: Spider名称,必需是唯一的
  • start_urls: 初始化下载连接URL
  • parse(): 用来解析下载后的Response对象,该对象也是这个办法的唯一参数。它负责解析返回页面数据并提取出相应的Item(返回Item对象),还有其他合法的连接URL(返回Request对象)。

我们在coolscrapy/spiders文件夹下面新建huxiu_spider.py,内容以下:

huxiu_spider.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
Topic: sample
Desc :
"""
from coolscrapy.items import HuxiuItem
import scrapy

class HuxiuSpider(scrapy.Spider):
name = "huxiu"
allowed_domains = ["huxiu.com"]
start_urls = [
"http://www.huxiu.com/index.php"
]

def parse(self, response):
for sel in response.xpath("//div[@class="mod-info-flow"]/div/div[@class="mob-ctt"]"):
item = HuxiuItem()
item["title"] = sel.xpath("h3/a/text()")[0].extract()
item["link"] = sel.xpath("h3/a/@href")[0].extract()
url = response.urljoin(item["link"])
item["desc"] = sel.xpath("div[@class="mob-sub"]/text()")[0].extract()
print(item["title"],item["link"],item["desc"])

运行爬虫

在根目录履行下面的命令,其中huxiu是你定义的spider名字:

1
scrapy crawl huxiu

如果一切正常,应当可以打印出每一个新闻

处置连接

如果想持续跟踪每一个新闻连接进去,看看它的详细内容的话,那末可以在parse()办法中返回一个Request对象,然后注册一个回调函数来解析新闻详情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from coolscrapy.items import HuxiuItem
import scrapy

class HuxiuSpider(scrapy.Spider):
name = "huxiu"
allowed_domains = ["huxiu.com"]
start_urls = [
"http://www.huxiu.com/index.php"
]

def parse(self, response):
for sel in response.xpath("//div[@class="mod-info-flow"]/div/div[@class="mob-ctt"]"):
item = HuxiuItem()
item["title"] = sel.xpath("h3/a/text()")[0].extract()
item["link"] = sel.xpath("h3/a/@href")[0].extract()
url = response.urljoin(item["link"])
item["desc"] = sel.xpath("div[@class="mob-sub"]/text()")[0].extract()
# print(item["title"],item["link"],item["desc"])
yield scrapy.Request(url, callback=self.parse_article)

def parse_article(self, response):
detail = response.xpath("//div[@class="article-wrap"]")
item = HuxiuItem()
item["title"] = detail.xpath("h1/text()")[0].extract()
item["link"] = response.url
item["posttime"] = detail.xpath(
"div[@class="article-author"]/span[@class="article-time"]/text()")[0].extract()
print(item["title"],item["link"],item["posttime"])
yield item

现在parse只提取感兴致的连接,然后将连接内容解析交给另外的办法去处置了。你可以基于这个构建更加庞杂的爬虫程序了。

导出抓取数据

最简略的保留抓取数据的方法是应用json格局的文件保留在本地,像下面这样运行:

1
scrapy crawl huxiu -o items.json

在演示的小体系里面这类方法足够了。不过如果你要构建庞杂的爬虫体系,最好自己编写Item Pipeline

保留数据到数据库

上面我们介绍了可以将抓取的Item导出为json格局的文件,不过最多见的做法还是编写Pipeline将其存储到数据库中。我们在coolscrapy/pipelines.py定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# -*- coding: utf-8 -*-
import datetime
import redis
import json
import logging
from contextlib import contextmanager

from scrapy import signals
from scrapy.exporters import JsonItemExporter
from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem
from sqlalchemy.orm import sessionmaker
from coolscrapy.models import News, db_connect, create_news_table, Article


class ArticleDataBasePipeline(object):
"""保留文章到数据库"""

def __init__(self):
engine = db_connect()
create_news_table(engine)
self.Session = sessionmaker(bind=engine)

def open_spider(self, spider):
"""This method is called when the spider is opened."""
pass

def process_item(self, item, spider):
a = Article(url=item["url"],
title=item["title"].encode("utf-8"),
publish_time=item["publish_time"].encode("utf-8"),
body=item["body"].encode("utf-8"),
source_site=item["source_site"].encode("utf-8"))
with session_scope(self.Session) as session:
session.add(a)

def close_spider(self, spider):
pass

上面我应用了python中的SQLAlchemy来保留数据库,这个是一个非常优良的ORM库,我写了篇关于它的入门教程,可以参考下。

然后在setting.py中配置这个Pipeline,还有数据库连接等信息:

1
2
3
4
5
6
7
8
9
10
11
12
ITEM_PIPELINES = {
"coolscrapy.pipelines.ArticleDataBasePipeline": 5,
}

# linux pip install MySQL-python
DATABASE = {"drivername": "mysql",
"host": "192.168.203.95",
"port": "3306",
"username": "root",
"password": "mysql",
"database": "spider",
"query": {"charset": "utf8"}}

再次运行爬虫

1
scrapy crawl huxiu

那末所有新闻的文章都存储到数据库中去了。

下一步

本章只是带你领略了scrapy最根本的功效,还有很多高等特征没有讲到。接下来会通过量个例子向你展现scrapy的其他特征,然后再深刻讲述每一个特征。