什么是scrapy ?
1 Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛
2 Scrapy 使用了 Twisted['twɪstɪd]
(其主要对手是Tornado)异步网络框架来处理网络通讯
3 Scrapy非常的灵活 ,我们可以自己修改一些参数,或者是自己写代码丰富,非常的方便这张图片是scrapy的流程图,开始看可能感觉 什么鬼,但是了解它的工作方式后,并不是那么的难以理解。
-
Scrapy Engine(引擎)
: 负责Spider
、ItemPipeline
、Downloader
、Scheduler
中间的通讯,信号、数据传递等。 -
Scheduler(调度器)
: 它负责接受引擎
发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎
需要时,交还给引擎
。 -
Downloader(下载器)
:负责下载Scrapy Engine(引擎)
发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎)
,由引擎
交给Spider
来处理, -
Spider(爬虫)
:它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎
,再次进入Scheduler(调度器)
, -
Item Pipeline(管道)
:它负责处理Spider
中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方. -
Downloader Middlewares(下载中间件)
:你可以当作是一个可以自定义扩展下载功能的组件。 -
Spider Middlewares(Spider中间件)
:你可以理解为是一个可以自定扩展和操作引擎
和Spider
中间通信
的功能组件(比如进入Spider
的Responses;和从Spider
出去的Requests)
Scrapy的安装
关于 Windows 和 linux 的安装 可以根据自己的操作系统进行安装,搜索一下其他安装博客,很多。我这就跳过
新建项目的简介 (linux操作系统为例)
当我们安装好scrapy 时候,我们可以通过命令快速的创建一个爬虫项目
scrapy startproject Spider_name >>>> (Spider_name为你的爬虫项目名称,可根据自己项目要求取名)
第一步 新建一个文件夹
第二步 我们通过命令新建一个爬虫项目
第三步 我们cd 到这个项目中去
第四步 我们tree一下看看项目的目录结构
发现这个架构的起始文件就这么几个,他们的具体是做什么用的:
scrapy.cfg :项目的配置文件 可以挂载项目到服务器
Yidianmsg/ :项目的Python模块
items.py :项目的目标文件 这里主要是定义我们要抓取的字段
pipelines.py :项目的管道文件 这里主要是文件的存储,以及其他的一些处理
settings.py :项目的设置文件 这里是项目的一些配置文件
spiders/ :存储爬虫代码目录 这里使我们写爬虫代码的地方,
项目新建完成了,但是spider目录里面是空的,我们怎么快速的创建一个爬虫,这里scrapy也提供了快捷键
当然了,也可以自己写,但是我更喜欢用命令直接创建,方便快捷,何乐不为。
scrapy有两个爬虫类 他们的创建方式不同
scrapy genspider spider_name xx.com # spider_name 是你爬虫名字,xx.com 允许爬虫爬取的域名
scrapy genspider -t crawl spider_name xxxx.com # 名称 和域名范围 快速创建命令
我们这里只说第一个spider类,也就是第一条命令,关于第二条命令 crawlspider有兴趣的课去查下,它的爬取速度更快,行为更为粗暴
项目和爬虫都建立完毕后我们会发现,spiders文件下多了一个爬虫文件,这是我们写爬虫的地方 名字为 yidianzixun.py
items.py 可以定义我们的爬取字段
pipelines.py 文件存储路径
import something
class SomethingPipeline(object):
def __init__(self):
# 可选实现,做参数初始化等
# doing something
def process_item(self, item, spider):
# item (Item 对象) – 被爬取的item
# spider (Spider 对象) – 爬取该item的spider
# 这个方法必须实现,每个item pipeline组件都需要调用该方法,
# 这个方法必须返回一个 Item 对象,被丢弃的item将不会被之后的pipeline组件所处理。
return item
def open_spider(self, spider):
# spider (Spider 对象) – 被开启的spider
# 可选实现,当spider被开启时,这个方法被调用。
def close_spider(self, spider):
# spider (Spider 对象) – 被关闭的spider
# 可选实现,当spider被关闭时,这个方法被调用
spider
文件里面的 .py文件是写爬虫的文件,name是爬虫的名称,allowed_domains允许的爬取域名,start_urls是起始url
里面的parse方法 是第一个回调函数(注意用crawlspider不能使用parse方法,源码中已经实现parse方法,重写会覆盖)
middlewares.py
这里是书写中间件的地方,这里的作用是下载器和引擎中间的钩子。在settings.py开启后我们发送的请求会经过这里,我们可以做一些处理,比如可以在这里加ip代理,user-agent等 可以参考以下图中实例
settings.py 常用设置
自上往下 【 user_agent ,自行设置】【是否遵守robots协议】【请求并发量,默认为16,可根据带宽自行调解】
【下载延迟,防止爬虫被网站屏蔽】【cookie】【添加默认的头部信息】【启用中间件】【启用pipeline】
下边我们以一点资讯为例 带来一个scrpy 爬虫实例
注:(代码仅作参考,有一些引用自写工具类却没有贴出来)
items.py
import scrapy
class YidianspiderItem(scrapy.Item):
# define the fields for your item here like:
name = scrapy.Field()
channel = scrapy.Field()
title = scrapy.Field()
item_type = scrapy.Field()
create_type = scrapy.Field()
original_url = scrapy.Field()
body = scrapy.Field()
images = scrapy.Field()
spider
# -*- coding: utf-8 -*-
# @Time : 18-5-23 下午2:10
# @Author : 杨星星
# @Email : yangshilong_liu@163.com
# @File : YidianSpider
# @Software: PyCharm
import logging
import scrapy
import re
from YidianSpider.items import YidianspiderItem
from YidianSpider.MyUtils import Util
class YidianSpider(scrapy.Spider):
name = 'yidian'
allowed_domains = ['yidianzixun.com']
start_urls = ['http://yidianzixun.com/']
child_channel_dicts = {"育儿":['t10449','e212806','t9651','u7916','u9392','u7934','e1595662','u7744','e268214','t19398','u7682','t10447','u7699']}
emotion_channel_dicts = {"情感":['u141','u9384','u575','u9387','u338','e2654','e288452','e929007','e158508','t9436','u655']}
headers = {
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1",
"Referer": "http://www.yidianzixun.com/",
}
def parse(self,response):
for channel, ids in self.child_channel_dicts.items():
for id in ids:
print('---id----', id)
url = "http://www.yidianzixun.com/channel/" + str(id)
yield scrapy.Request(url=url, callback=self.parse_page_links,headers=self.headers, meta={"channel": channel})
def parse_page_links(self,response):
channel = response.meta['channel']
# print(channel)
# url = response.url
response_html = response.body.decode('utf-8')
article_links = re.findall(r'href="(.*?)"',response_html)
for article_link in article_links:
if 'article' in article_link:
article_url = "http://www.yidianzixun.com" + str(article_link)
print('--flag1---url----',article_url)
yield scrapy.Request(url=article_url,callback=self.parse_article_detail,meta={"channel": channel},headers=self.headers)
else:
continue
def parse_article_detail(self,response):
channel = response.meta['channel']
item = YidianspiderItem()
# class="doc-source">福建吃喝玩乐</ <div class="source imedia">福建吃喝玩乐</div><
name = re.findall(r'<div class="source imedia">(.*?)</div>', response.body.decode('utf-8'))[0]
title = re.findall(r'<h3>(.*?)</h3>', response.body.decode('utf-8'))[0]
OriginalBody = re.findall(r'<body>(.*?)</body>', response.body.decode('utf-8'))[0]
NeedRepImageUrls = re.findall(r'"url":"(.*?)"', re.findall(r'"images":\[(.*?)\]', response.body.decode('utf-8'))[0])
BodyFlags = re.findall(r'<div id="article-img-\d+"class="a-image" .*?></div>', response.body.decode('utf-8'))
if '.mp4' in OriginalBody:
print('>>>----视频资源---PASS--')
return
else:
rep_body = OriginalBody
for flag, image in zip(BodyFlags, NeedRepImageUrls):
rep_body = rep_body.replace(flag, '<img src="http:' + image + '">')
RemoveBodyId = re.sub(r'id=".*?"', '', rep_body)
RemoveBodyClass = re.sub(r'class=".*?"', '', RemoveBodyId)
ImageUrl = re.findall(r'src="(.*?)"', RemoveBodyClass)
item['image_urls'] = ImageUrl
try:
tag = Util.get_tags_by_jieba(RemoveBodyClass)
if tag:
tag.append(channel)
item['tag'] = tag
except Exception as error:
logging.info(error,'---tag--error--')
return
if title:
item['title'] = title
else:
return
try:
Temp_Content = RemoveBodyClass
for content_img_url in ImageUrl:
content_url_temp = Util.generate_pic_time_yidian_lazy(content_img_url)
Temp_Content = Temp_Content.replace(content_img_url, content_url_temp)
item['body'] = Temp_Content
except Exception as e:
print(e)
logging.info('----BODY---ERROR--')
return
try:
item['images'] = []
data_echo_url = re.findall(r'data-echo="(.*?)"', item['body'])
for num, image_url in enumerate(data_echo_url):
temp_image_url = Util.generate_pic_time_yd(image_url) + '.jpg'
image_dict = {"index": num, "url": temp_image_url, "title": ""}
item['images'].append(image_dict)
except Exception as e:
print('---ERROR---', e, '--IMAGES---ERROR---')
logging.info('---IMAGES--ERROR---')
return
item['channel'] = channel
item['source'] = '一点号'
if name:
item['name'] = name
else:
item['name'] = '一点号'
item['original_url'] = response.url
if not item['body']:
return
if len(item['body']) < 200:
return
CheckItem = Util.check_item(item)
print('---',CheckItem ,'---')
if CheckItem == 1:
return
yield item
pipeline.py
import json
class NewsPipeline(object):
def open_spider(self, spider):
self.filename = open('data.json', 'w')
def process_item(self, item, spider):
content = json.dumps(dict(item)) + "\n"
self.filename.write(content.encode('utf-8').decode('unicode-escape'))
return item
def close_spider(self, spider):
self.filename.close()
settings.py
# -*- coding: utf-8 -*-
# Scrapy settings for YidianSpider project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
# https://doc.scrapy.org/en/latest/topics/settings.html
# https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
# https://doc.scrapy.org/en/latest/topics/spider-middleware.html
BOT_NAME = 'YidianSpider'
SPIDER_MODULES = ['YidianSpider.spiders']
NEWSPIDER_MODULE = 'YidianSpider.spiders'
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# Configure maximum concurrent requests performed by Scrapy (default: 16)
CONCURRENT_REQUESTS = 16
# Configure a delay for requests for the same website (default: 0)
# See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 2
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16
# Disable cookies (enabled by default)
#COOKIES_ENABLED = False
# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False
# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# 'Accept-Language': 'en',
#}
# Enable or disable spider middlewares
# See https://doc.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
# 'YidianSpider.middlewares.YidianspiderSpiderMiddleware': 543,
#}
# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
'YidianSpider.middlewares.YidianspiderDownloaderMiddleware': 543,
}
# Enable or disable extensions
# See https://doc.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
# 'scrapy.extensions.telnet.TelnetConsole': None,
#}
# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'YidianSpider.pipelines.YidianspiderPipeline': 300,
}