Py007-02-10scrapy之日志等级

日志

回顾我们之前的爬虫命令

1
2
3
scrapy crawl 应用名  --nolog

# 此时的打印信息 很多完全看不懂!

日志等级(种类)

  • ERROR:错误
  • WARNING:警告
  • INFO:一般信息
  • DEBUG:调试信息(默认)

控制日志的打印 settings.py

  • LOG_LEVEL 指定输出某一种信息
  • LOG_FILE 将日志信息存到一个文件里
1
2
3
# 添加 LOG_LEVEL = '种类' 来控制显示哪一层级的日志
LOG_LEVEL = 'ERROR'
# 此时只有错误的时候才会打印日志

将日志信息存到一个文件里

1
LOG_FILE = 'log.txt'

Py007-02-09scrapy之代理操作

代理操作

这个url可以看本机ip

代理操作初步——先看本机ip

新建工程

1
2
3
4
5
scrapy startproject proxyPro

cd proxyPro

scrapy genspider proxyDemo www.baidu.com/s?wd=ip

修改 生成的spiders/proxyDemo.py

1
2
3
4
5
6
7
8
9
10
import scrapy

class ProxydemoSpider(scrapy.Spider):
name = 'proxyDemo'
# allowed_domains = ['www.baidu.com/s?wd=ip']
start_urls = ['https://www.baidu.com/s?wd=ip']

def parse(self, response):
fp = open('proxy.html','w',encoding='utf-8')
fp.write(response.text)

修改一些配置 settings.py

1
2
3
4
5
# 伪装请求载体身份
19行:USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'

22行:ROBOTSTXT_OBEY = False #可以忽略或者不遵守robots协议
# 不遵守robots协议

执行爬虫命令

1
2
3
scrapy crawl proxyDemo --nolog

# 查看 proxy.html 看到本机ip

代理——中间件

middlewares.py 的修改

自定义下载中间件的类,在类中事先 process_request (处理中间拦截到的请求的)方法

1
2
3
4
5
6
7
8
9
10
11
class MyProxy(object):
# 此时的process_request 依旧是对父类方法的重写
def process_request(self,request,spider):
# 请求ip的更换
'''
去 代理ip网 找个代理
http://www.goubanjia.com/
'''
request.meta['proxy'] = 'https://123.1.150.244:80'

# 之前其他的无用代码可以移除

去settings.py里继续配置

1
2
3
4
5
6
7
8
9
10
# 找到 下载中间件 DOWNLOADER_MIDDLEWARES
# 这个是之前 middlewares.py里的 下载中间件
DOWNLOADER_MIDDLEWARES = {
'proxyPro.middlewares.ProxyproDownloaderMiddleware': 543,
}

# 修改成我们自己定义的 中间件类
DOWNLOADER_MIDDLEWARES = {
'proxyPro.middlewares.MyProxy': 543,
}

执行爬虫命令(此时建议 不携带–nolog参数,因为代理ip可能会失败)

1
scrapy crawl proxyDemo

注意点

1
代理和请求的url的协议头  要一致  要么是http ,要么是https

总结

1.下载中间件类的自定义:

  • 继承 object
  • 重写 process_request(self,request,spider) 方法

2.配置文件中 开启下载中间件

Py007-02-08scrapy之cookie操作

cookie操作

豆瓣登陆指定页面

1
2
3
4
5
scrapy startproject doubanPro

cd doubanPro

scrapy genspider douban www.douban.com

第一步 先该settins.py里的一些配置

1
2
3
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'

ROBOTSTXT_OBEY = False

第二步

  • 修改起始url 也就是登陆的url
  • 重写 start_requests方法 发起post请求 (这里有登录操作,携带post参数)
  • 登录成功后 通过yield 来发送第二个请求 也就是个人主页页面数据
  • 自定义 请求成功后的parse方法 来进行个人页面数据的解析操作
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 scrapy


class DoubanSpider(scrapy.Spider):
name = 'douban'
allowed_domains = ['www.douban.com']
start_urls = ['http://www.douban.com/accounts/login']

# 重写 start_requests
def start_requests(self):
# 将请求参数封装到 字典中
# 豆瓣登陆页面 https://accounts.douban.com/login
data = {
'source': 'index_nav',
'form_email': '15027900535',
'form_password': 'bobo@15027900535',
}
for url in self.start_urls:
yield scrapy.FormRequest(url=url,formdata=data,callback=self.parse)
def parse(self, response):

# 登陆成功后的页面数据进行存储

fp = open('main.html','w',encoding='utf-8')
fp.write(response.text)

# 获取当前用户个人主页对应的页面数据
url = 'https://www.douban.com/people/185687620/'

yield scrapy.Request(url=url,callback=self.parseSecondPage)

# 定义跳转指定页面的 parse方法
def parseSecondPage(self,response):
fp = open('second.html', 'w', encoding='utf-8')
fp.write(response.text)

# 可以对当前用户的个人主页的页面数据进行解析操作

第三步 执行爬虫命令

1
scrapy crawl douban --nolog

总结

通过scrapy进行cookie操作时,并不需要将cookie进行特定的提取,也不需要将cookie加载到我们的请求对象中去

  • 涉及登录的时候,如果服务器给我们设置了cookie,scrapy在第二次请求页面的时候自动帮我们携带cookie

Py007-02-07scrapy发post请求

scrapy里post请求

1
2
3
4
5
scrapy startproject postPro

cd postPro

scrapy genspider postDemo www.baidu.com

start_requests方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PostdemoSpider(scrapy.Spider):
name = 'postDemo'
# allowed_domains = ['www.baidu.com']
start_urls = ['https://fanyi.baidu.com/sug']

# start_requests是父类的方法:对start_urls里的url进行get请求发送
'''
# 大概实现如下
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(url=url,callback=self.parse)
'''

def parse(self, response):
pass

发送post请求

start_requests 默认发送get 如何发送post呢?

重写 start_requests 方法的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
# 发起post
'''
1.将Request方法的method赋值成post(不推荐)
2.FormRequest() 这个方法也可以发起post请求(推荐)
'''
def start_requests(self):
# post请求参数
data = {
'kw':'dog'
}
for url in self.start_urls:
# formdata :请求参数对应的字典
yield scrapy.FormRequest(url=url,formdata=data,callback=self.parse)

需求:百度翻译中指定词条的翻译结果 进行获取

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
# -*- coding: utf-8 -*-
import scrapy

class PostdemoSpider(scrapy.Spider):
name = 'postDemo'
# allowed_domains = ['www.baidu.com']
start_urls = ['https://fanyi.baidu.com/sug']

# start_requests是父类的方法:对start_urls里的url进行get请求发送
'''
# 大概实现如下
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(url=url,callback=self.parse)
'''

# 发起post
'''
1.将Request方法的method赋值成post
2.FormRequest() 这个方法也可以发起post请求(推荐)
'''
def start_requests(self):
# post请求参数
data = {
'kw':'dog'
}
for url in self.start_urls:
# formdata :请求参数对应的字典
yield scrapy.FormRequest(url=url,formdata=data,callback=self.parse)

def parse(self, response):
print(response.text)
pass

注意:别忘了修改settings.py里的设置

1
2
3
4
5
# 伪装请求载体身份
19行:USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'

22行:ROBOTSTXT_OBEY = False #可以忽略或者不遵守robots协议
# 不遵守robots协议

验证结果

1
scrapy crawl postDemo --nolog

总结

如何发post方法?

一定要对start_requests方法进行重写

  • 1.将Request方法的method赋值成post
  • 2.FormRequest() 这个方法也可以发起post请求(推荐)

Py007-02-06scrapy核心组件

scrapy五大核心组件

引擎(Scrapy)

  • 用来处理整个系统的数据流处理, 触发事务(框架核心)

调度器(Scheduler)

  • 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址

下载器(Downloader)

  • 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)

爬虫(Spiders)

  • 爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面

项目管道(Pipeline)

  • 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。

Py007-02-05scrapy多个url爬取

爬取多个页面url数据

1
2
# 糗事百科  文字标签页面的数据   它有13页
https://www.qiushibaike.com/text/

项目实战

1
2
3
4
5
6
7
8
9
10
11
12
13
# 切换到桌面
cd ~/Desktop


# 创建项目
scarpy startproject qiubaiByPages


# 创建成功后进入该目录
cd qiubaiByPages/

# 创建spider 爬虫应用
scrapy genspider qiubai www.qiushibaike.com/text

第一步 修改spiders/qiubai.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import scrapy

class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
allowed_domains = ['www.qiushibaike.com/text']
start_urls = ['https://www.qiushibaike.com/text/']

def parse(self, response):
div_list = response.xpath('//*[@id="content-left"]/div')

for div in div_list:
author = div.xpath('./div[@class="author clearfix"]/a[2]/h2/text()').extract_first()
content = div.xpath('.//div[@class="content"]/span/text()').extract_first()

# 创建items对象,将解析内容存储到items对象里

pass

第二步 创建items对象 items.py

1
2
3
4
5
import scrapy

class QiubaibypagesItem(scrapy.Item):
author = scrapy.Field()
content = scrapy.Field()

第三步 在spiders/qiubai.py里导入items,并把item数据提交给管道文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import scrapy
from qiubaiByPages.items import QiubaibypagesItem

class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
# 建议注释掉 allowed_domains 图片的资源域名可能与此不一致
# allowed_domains = ['www.qiushibaike.com/text']
start_urls = ['https://www.qiushibaike.com/text/']

def parse(self, response):
div_list = response.xpath('//*[@id="content-left"]/div')

for div in div_list:
author = div.xpath('./div[@class="author clearfix"]/a[2]/h2/text()').extract_first()
content = div.xpath('.//div[@class="content"]/span/text()').extract_first()

# 创建items对象,将解析内容存储到items对象里
item = QiubaibypagesItem()
item['author'] = author
item['content'] = content

# 将item对象提交给管道文件
yield item

修改管道文件pipelines.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# -*- coding: utf-8 -*-

class QiubaibypagesPipeline(object):
fp = None
def open_spider(self,spider):
print('开始爬虫')
self.fp = open('./qiubai.txt','w',encoding='utf-8')

def close_spider(self,spider):
print('结束爬虫')
self.fp.close()

def process_item(self, item, spider):
self.fp.write(item['author']+':'+item['content']+'\n\n\n')
return item

第四步 修改配置文件settings.py

1
2
3
4
5
6
7
8
9
10
11
# 伪装请求载体身份
19行:USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'

22行:ROBOTSTXT_OBEY = False #可以忽略或者不遵守robots协议
# 不遵守robots协议


# 解开注释 管道操作配置
ITEM_PIPELINES = {
'qiubaiByPages.pipelines.QiubaibypagesPipeline': 300,
}

最后 回到刚刚的命令行 执行爬虫命令

把基础的流程跑通

1
scrapy crawl qiubai --nolog

第二部分

  • 上述流程跑通后,仅仅是一页的数据而
  • 我们的需求是它的所有分页数据
1
2
# 该url的分页数据
https://www.qiushibaike.com/text/

解决方案

  • 请求的手动发送

分析spiders/qiubai.py

1
2
3
4
5
6
7
8
9
10
def parse(self, response):
...
# 将item对象提交给管道文件
yield item
'''
当yield item都执行完毕 意味着:

https://www.qiushibaike.com/text/
这一页面的数据爬取完毕
'''

手动处理下一次请求(仅仅以第二页数据为例)理解版本的parse函数修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def parse(self, response):
div_list = response.xpath('//*[@id="content-left"]/div')

for div in div_list:
author = div.xpath('./div[@class="author clearfix"]/a[2]/h2/text()').extract_first()
content = div.xpath('.//div[@class="content"]/span/text()').extract_first()

# 创建items对象,将解析内容存储到items对象里
item = QiubaibypagesItem()
item['author'] = author
item['content'] = content

# 将item对象提交给管道文件
yield item

# 请求的手动发送
url = 'https://www.qiushibaike.com/text/page/2'
'''
url代表下一次请求的 url
callback代表将请求到的页面数据进行解析 而这个解析函数正是我们这里的parse函数
'''
yield scrapy.Request(url=url,callback=self.parse)
注意 这个版本 手动发送请求属于递归调用 由于没有结束条件所以会一直递归下去。。。。。
注意 这个版本 手动发送请求属于递归调用 由于没有结束条件所以会一直递归下去。。。。。
注意 这个版本 手动发送请求属于递归调用 由于没有结束条件所以会一直递归下去。。。。。

完整版的qiubai.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
import scrapy
from qiubaiByPages.items import QiubaibypagesItem

class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
# 注视掉这个 allowed_domains
# allowed_domains = ['www.qiushibaike.com/text']
start_urls = ['https://www.qiushibaike.com/text/']

# 设计通用的url模版
url = 'https://www.qiushibaike.com/text/page/%d'
pageNum = 1

def parse(self, response):
div_list = response.xpath('//*[@id="content-left"]/div')

for div in div_list:
author = div.xpath('./div[@class="author clearfix"]/a[2]/h2/text()').extract_first()
content = div.xpath('.//div[@class="content"]/span/text()').extract_first()

# 创建items对象,将解析内容存储到items对象里
item = QiubaibypagesItem()
item['author'] = author
item['content'] = content

# 将item对象提交给管道文件
yield item

# 请求的手动发送
if self.pageNum < 13: # 13表示是最后一页的页码
print('爬取到了第%d的页面数据'%self.pageNum)
self.pageNum += 1 # 页码增加
new_url = format(self.url%self.pageNum)
yield scrapy.Request(url=new_url,callback=self.parse)

Py007-02-04scrapy管道高级操作

需求:将爬取的数据分别存到本地磁盘/redis数据库/mysql数据库

  1. 需要在管道文件 pipelines.py 里编写对应的管道类
  2. 在配置文件里 settings.py 对自定义管道类进行生效操作

依旧基于之前的qiubaiPro项目

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# -*- coding: utf-8 -*-

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

# 基于管道 文件存储
# class QiubaiproPipeline(object):
# fp = None
# # 在爬虫过程中,该方法只会在开始爬虫的时候调用一次
# def open_spider(self,spider):
# print('开始爬虫')
# self.fp = open('./qiubai_pipe.txt', 'w', encoding='utf-8')
#
# # 在爬虫过程中,该方法只会在爬虫结束的时候调用一次
# def close_spider(self, spider):
# print('结束爬虫')
# self.fp.close()
#
# # 该方法接收 爬虫文件中提交过来的item对象,并对item对象中存储的数据进行持久化存储
# '''
# :param
# # 参数item:表示接收到的item对象
# '''
# def process_item(self, item, spider):
# # 取出item对应的数据值
# author = item['author']
# content = item['content']
#
# # 持久化存储
# self.fp.write(author+':'+content+'\n\n\n')
# return item


# # mysql
# import pymysql
#
# class QiubaiproPipeline(object):
# conn = None
# # 在爬虫过程中,该方法只会在开始爬虫的时候调用一次
# def open_spider(self,spider):
# print('开始爬虫')
# # 连接数据库
# self.conn = pymysql.connect(host='127.0.0.1',port=3306,user='root',password='831015',db='qiubai')
#
#
# # 在爬虫过程中,该方法只会在爬虫结束的时候调用一次
# def close_spider(self, spider):
# print('结束爬虫')
# self.cursor.close()
# self.conn.close()
#
# # 该方法接收 爬虫文件中提交过来的item对象,并对item对象中存储的数据进行持久化存储
# '''
# :param
# # 参数item:表示接收到的item对象
# '''
# def process_item(self, item, spider):
# # 取出item对应的数据值
# author = item['author']
# content = item['content']
#
# # 连接数据库
# # 执行sql
# sql = 'insert into qiubai values(null,"%s","%s")'%(author,content)
# self.cursor = self.conn.cursor()
# try:
# self.cursor.execute(sql)
# self.conn.commit()
# # 提交事务
# except Exception as e:
# print(e)
# self.conn.rollback()
#
# return item

# redis 安装 pip3 install redis
import redis
import json
class QiubaiproPipeline(object):
conn = None
# 在爬虫过程中,该方法只会在开始爬虫的时候调用一次
def open_spider(self,spider):
print('开始爬虫')
# 连接数据库

self.conn = redis.Redis(host='127.0.0.1', port=6379)

# 在爬虫过程中,该方法只会在爬虫结束的时候调用一次
def close_spider(self, spider):
print('结束爬虫')
print(self.conn.lrange('data',0,-1))

# 该方法接收 爬虫文件中提交过来的item对象,并对item对象中存储的数据进行持久化存储
'''
:param
# 参数item:表示接收到的item对象
'''
def process_item(self, item, spider):
# 取出item对应的数据值
author = item['author']
content = item['content']

obj = {
'author':author,
'content':content
}
# 录入数据
# self.conn.lpush('data', obj) 5.0版本报错 发现 序列化之后就好了
self.conn.lpush('data',json.dumps(obj))
return item


# 封装另外的类 存储操作

# 将数据存入到本地磁盘
class QiubaiByFiles(object):
def process_item(self,item,spider):
print('数据写入磁盘')
return item

# 将数据存入到 Mysql
class QiubaiByMySql(object):
def process_item(self,item,spider):
print('数据写入mysql')
return item


# 将数据存入到 Redis
class QiubaiByRedis(object):
def process_item(self,item,spider):
print('数据写入redis')
return item

如何将自定义的类 加入管道呢?

settings.py里

1
2
3
4
5
6
7
8
9
10
11
# settings.py里 ITEM_PIPELINES 进行配置

ITEM_PIPELINES = {
'qiubaiPro.pipelines.QiubaiproPipeline': 300,
'qiubaiPro.pipelines.QiubaiByFiles': 400,
'qiubaiPro.pipelines.QiubaiByMySql': 500,
'qiubaiPro.pipelines.QiubaiByRedis': 600,
}

# 300代表执行的优先级
# 此时 QiubaiByRedis的优先级最高

Py007-02-03redis安装以及使用

redis

1
2
3
4
自行去官网下载对应安装文件 我下的是5.0版本
https://redis.io/

http://www.redis.cn/

下载完成后解压缩后===> redis-5.0.0

1
2
3
4
5
6
7
8
9
10
该目录文件如下:
00-RELEASENOTES Makefile runtest-sentinel
BUGS README.md sentinel.conf
CONTRIBUTING deps src
COPYING redis.conf tests
INSTALL runtest utils

---------------------------------------------------
redis.conf这是一个配置文件(分布式爬虫时会要求对其进行配置)
src目录 这里是redis的源码文件 需要编译才能变成使用

安装redis

1
2
3
4
5
6
7
8
# 进入redis5.0.0 这个目录
# 执行
make
# 成功之后再次进入 redis5.0.0/src
# 发现多了几个命令行文件
# 其中有俩重要的文件
redis-server 用来打开redis服务器
redis-cli 打开redis的客户端

启动redis服务器

  • redis-server 通常结合一个配置(上一级目录的redis.conf)然后启动
1
2
3
4
5
# 进入reids-server目录
cd redis5.0.0/src

./redis-server ../redis.conf
# 看到一个正方体 代表你启动成功

启动redis客户端

再开一个终端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 进入reids-server目录
cd redis5.0.0/src

redis-cli

成功之后就可以进行相应的操作

如输入

> set name 'bobo'
> ok

> get name
> 'bobo'

实战

依旧基于之前的糗百爬虫项目

修改 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
# redis   安装 pip3 install redis
import redis

class QiubaiproPipeline(object):
conn = None
# 在爬虫过程中,该方法只会在开始爬虫的时候调用一次
def open_spider(self,spider):
print('开始爬虫')
# 连接数据库
self.conn = redis.Redis(host='127.0.0.1',port=6379)

# 在爬虫过程中,该方法只会在爬虫结束的时候调用一次
def close_spider(self, spider):
print('结束爬虫')

# 该方法接收 爬虫文件中提交过来的item对象,并对item对象中存储的数据进行持久化存储
'''
:param
# 参数item:表示接收到的item对象
'''
def process_item(self, item, spider):
# 取出item对应的数据值
author = item['author']
content = item['content']

obj = {
'author':author,
'content':content
}
# 录入数据
self.conn.lpush('data',obj)

return item

启动redis服务

1
2
3
cd redis5.0.0/src

./redis-server ../redis.conf

执行爬虫程序

1
scrapy crawl qiubai --nolog

启动redis客户端

1
2
3
4
5
cd redis5.0.0/src

./redis-cli
# 查看所有列表的数据
lrange data 0 -1

Py007-02-02scrapy持久化存储

持久化存储操作

两种方式

  • a 磁盘文件
  • b 数据库

磁盘文件方式

  • 基于终端指令的方式
  • 基于管道的方式

终端指令的方式

  • 保证爬虫文件的parse方法中有可迭代类型对象(通常为列表or字典)的返回,该返回值
  • 通过终端指令的形式写入指定格式的文件中进行持久化操作。

基于上一篇内容来处理

qiubai.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
import scrapy

class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
# 最好注释这个允许域名 因为图片如果是其他域名的就获取不到了
# allowed_domains = ['www.qiushibaike.com/text']

# 默认生成的协议是http的需要手动修改为 https
start_urls = ['https://www.qiushibaike.com/text/']

def parse(self, response):
# 推荐xpath(scrapy里集成了xpath解析接口)

# 每个段子的外层容器
div_list = response.xpath('//div[@id="content-left"]/div')

# step001 数据容器(可迭代对象)
data_list = []
for div in div_list:
author = div.xpath('./div/a[2]/h2/text()').extract_first()
content = div.xpath('.//div[@class="content"]/span/text()').extract_first()

obj = {
'author':author,
'content':content
}
data_list.append(obj)

# step002 返回可迭代对象
return data_list

通过命令来存储数据

执行输出指定格式进行存储:将爬取到的数据写入不同格式的文件中进行存储

1
2
3
4
5
6
7
8
9
10
11
'''
scrapy crawl 爬虫名称 -o xxx.json
scrapy crawl 爬虫名称 -o xxx.xml
scrapy crawl 爬虫名称 -o xxx.csv
'''

scrapy crawl qiubai -o qiubai.csv --nolog

scrapy crawl qiubai -o qiubai.txt --nolog

scrapy crawl qiubai -o qiubai.json --nolog

基于管道

  • 1.items 存储解析到的页面数据 类似django里的models
  • 2.pipelines 处理持久化存储的相关操作
  • 3.代码实现流程
    1
    2
    3
    4
    1. 将解析的数据存到 items对象
    2. 使用yield 关键字将 items提交给管道文件进行处理
    3. 在管道文件中编写代码完成数据存储
    4. 在配置文件里开启'管道'操作

继续操作刚刚的爬虫文件

第一步 将解析的数据存到 items对象

step001 qiubai.py
1
2
# 引入items模块
from qiubaiPro.qiubaiPro.items import QiubaiproItem
step002 声明items对应的字段 如 author和 content

items.py

1
2
3
4
5
6
import scrapy

class QiubaiproItem(scrapy.Item):
# 声明字段
author = scrapy.Field()
content = scrapy.Field()
step003添加 数据到items
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import scrapy
from qiubaiPro.items import QiubaiproItem

class QiubaiSpider(scrapy.Spider):
name = 'qiubai'

start_urls = ['https://www.qiushibaike.com/text/']

def parse(self, response):
# 推荐xpath(scrapy里集成了xpath解析接口)

# 每个段子的外层容器
div_list = response.xpath('//div[@id="content-left"]/div')

for div in div_list:
author = div.xpath('./div/a[2]/h2/text()').extract_first()
content = div.xpath('.//div[@class="content"]/span/text()').extract_first()

# step001 将解析到的数据 存到items对象
item = QiubaiproItem()
item['author'] = author
item['content'] = content

第二步 :使用yield 关键字将 items提交给管道文件进行处理

1
2
3
4
5
6
7
8
9
# 将解析到的数据 存到items对象
item = QiubaiproItem()
item['author'] = author
item['content'] = content

# step002 将item对象提交给管道
yield item

# 去编写对象的 pipelines.py

第三步 :在管道文件中编写代码完成数据存储

pipelines.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class QiubaiproPipeline(object):
# 该方法接收 爬虫文件中提交过来的item对象,并对item对象中存储的数据进行持久化存储
'''
:param
# 参数item:表示接收到的item对象
'''
# 每向管道文件提交一次item,则该方法就会执行一次
def process_item(self, item, spider):
# 取出item对应的数据值
author = item['author']
content = item['content']

# 持久化存储
with open('./qiubai_pipe.txt','w',encoding='utf-8') as fp:
fp.write(author+':'+content+'\n\n\n')
return item

第四步 在配置文件里开启’管道’操作

settings.py

1
2
3
4
5
6
7
8
# 搜索pipeline

# 解开注释
ITEM_PIPELINES = {
'qiubaiPro.pipelines.QiubaiproPipeline': 300,
}

# 300代表优先级

执行爬虫命令

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
39
40
scrapy crawl qiubai 

# 此时发现qiubai_pipe.txt里只有一条数据

# 原因是
'''
qiubai.py里的 yield item 写在for循环里

def parse(self, response):
# 推荐xpath(scrapy里集成了xpath解析接口)

# 每个段子的外层容器
div_list = response.xpath('//div[@id="content-left"]/div')

for div in div_list:
author = div.xpath('./div/a[2]/h2/text()').extract_first()
content = div.xpath('.//div[@class="content"]/span/text()').extract_first()

# step001 将解析到的数据 存到items对象
item = QiubaiproItem()
item['author'] = author
item['content'] = content

# step002 将item对象提交给管道
yield item
------------------------------------------
每向管道文件提交一次item,则该方法就会执行一次
pipelines.py里的

def process_item(self, item, spider):
# 取出item对应的数据值
author = item['author']
content = item['content']

# 持久化存储
with open('./qiubai_pipe.txt','w',encoding='utf-8') as fp:
fp.write(author+':'+content+'\n\n\n')
return item
所以只存了 最后一次的爬虫数据
'''

解决存储bug

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
class QiubaiproPipeline(object):
fp = None
# 在爬虫过程中,该方法只会在开始爬虫的时候调用一次
def open_spider(self,spider):
print('开始爬虫')
self.fp = open('./qiubai_pipe.txt', 'w', encoding='utf-8')

# 在爬虫过程中,该方法只会在爬虫结束的时候调用一次
def close_spider(self, spider):
print('结束爬虫')
self.fp.close()

# 该方法接收 爬虫文件中提交过来的item对象,并对item对象中存储的数据进行持久化存储
'''
:param
# 参数item:表示接收到的item对象
'''
def process_item(self, item, spider):
# 取出item对应的数据值
author = item['author']
content = item['content']

# 持久化存储
self.fp.write(author+':'+content+'\n\n\n')
return item

再次执行爬虫命令

1
scrapy crawl qiubai --nolog

基于mysql持久化存储

与管道方式大体相同,唯一不同的是第三步 此时不是存入文件而是 sql录入

1
2
3
4
1. 将解析的数据存到 items对象
2. 使用yield 关键字将 items提交给管道文件进行处理
3. 在管道文件中编写代码sql录入语句完成数据存储
4. 在配置文件里开启'管道'操作
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
39
40
41
import pymysql
class QiubaiproPipeline(object):
conn = None
# 在爬虫过程中,该方法只会在开始爬虫的时候调用一次
def open_spider(self,spider):
print('开始爬虫')
# 连接数据库
self.conn = pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='831015',db='qiubai')


# 在爬虫过程中,该方法只会在爬虫结束的时候调用一次
def close_spider(self, spider):
print('结束爬虫')
self.cursor.close()
self.conn.close()

# 该方法接收 爬虫文件中提交过来的item对象,并对item对象中存储的数据进行持久化存储
'''
:param
# 参数item:表示接收到的item对象
'''
def process_item(self, item, spider):
# 取出item对应的数据值
author = item['author']
content = item['content']

# 连接数据库
# 执行sql
sql = 'insert into qiubai values(null,"%s","%s")'%(author,content)
self.cursor = self.conn.cursor()
try:
self.cursor.execute(sql)
self.conn.commit()
# 提交事务
except Exception as e:
print(e)
self.conn.rollback()


self.fp.write(author+':'+content+'\n\n\n')
return item
  • 建库 建表
1
2
3
4
5
6
7
8
9
10
11
mysql -uroot -pxxx

create database qiubai;

use qiubai

create table qiubai (
id int primary key auto_increment,
author varchar(100) not null,
content varchar(500) not null
);

执行爬虫命令

1
scrapy crawl qiubai --nolog

mysql终端里

1
select * from qiubai;

Py007-02-01scrapy简介

scrapy简介

  • 环境安装
  • 基础使用

一.什么是Scrapy?

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架,非常出名,非常强悍。所谓的框架就是一个已经被集成了各种功能(高性能异步下载,队列,分布式,解析,持久化等)的具有很强通用性的项目模板。对于框架的学习,重点是要学习其框架的特性、各个功能的用法即可。

二.安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  Linux:

pip3 install scrapy



  Windows:

a. pip3 install wheel

b. 下载twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

c. 进入下载目录,执行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl

d. pip3 install pywin32

e. pip3 install scrapy

三.基础使用

1.创建项目:scrapy startproject 项目名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
项目结构:

project_name/
scrapy.cfg:
project_name/
__init__.py
items.py
pipelines.py
settings.py
spiders/
__init__.py

scrapy.cfg 项目的主配置信息。(真正爬虫相关的配置信息在settings.py文件中)
items.py 设置数据存储模板,用于结构化数据,如:Django的Model
pipelines 数据持久化处理
settings.py 配置文件,如:递归的层数、并发数,延迟下载等
spiders 爬虫目录,如:创建文件,编写爬虫解析规则

 2.创建爬虫应用程序:

1
2
3
cd project_name(进入项目目录)

scrapy genspider 应用名称 爬取网页的起始url (例如:scrapy genspider qiubai www.qiushibaike.com)

3.编写爬虫文件:在步骤2执行完毕后,会在项目的spiders中生成一个应用名的py爬虫文件,文件源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding: utf-8 -*-
import scrapy

class QiubaiSpider(scrapy.Spider):
#爬虫文件名称 :通过这个name可以指定的定位到某一个具体的爬虫文件
name = 'qiubai'
#允许的域名(爬取指定域名下的页面数据)
allowed_domains = ['https://www.qiushibaike.com/']
#起始url: 列表的形式,当前工程将要爬取页面对应的url
start_urls = ['https://www.qiushibaike.com/']

'''
# 不能对百度进行爬取 因为allowed_domains的域名 跟百度不是一个域名
start_urls = ['https://www.qiushibaike.com/','www.baidu.com']
'''

# (回调)解析方法:对获取的页面数据进行指定内容的解析
# response:根据起始url列表发请求,请求成功后返回的响应对象
# 该函数返回值必须为可迭代对象或者NUll
def parse(self, response):
print(response.text) #获取字符串类型的响应内容
print(response.body)#获取字节类型的相应内容

4.设置修改settings.py配置文件相关配置:

修改内容及其结果如下:(初学阶段仅仅做如下设置)

1
2
3
4
5
# 伪装请求载体身份
19行:USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'

22行:ROBOTSTXT_OBEY = False #可以忽略或者不遵守robots协议
# 不遵守robots协议

5.执行爬虫程序:

1
2
3
4
scrapy crawl  应用名称

scrapy crawl qiubai
# 会显示一堆 日志信息

忽略日志信息

1
scrapy crawl 应用名称 --nolog

四.小试牛刀:将糗百首页中文字标签对应的内容和标题进行爬取

1. 创建工程

1
scrapy startproject qiubaiPro

2. 创建一个爬虫程序

1
2
3
# 进入到工程目录
cd qiubaiPro
scrapy genspider qiubai www.qiushibaike.com/text

3. 编写代码

  • 注意默认的协议是http 所以需要手动改对应的网址协议
1
2
3
4
# 默认
start_urls = ['http://www.qiushibaike.com/']
# 更改后
start_urls = ['https://www.qiushibaike.com/']
  • 建议注释掉 allowed_domains
1
2
# 因为爬取内容的图片不一定是本域名下的  如果设置了  可能获取不到
allowed_domains = ['https://www.qiushibaike.com/']
  • 解析方式
1
推荐  xpath (scrapy里集成了  xpath解析接口)
  • 修改配置
1
2
3
4
5
# 伪装请求载体身份
19行:USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'

22行:ROBOTSTXT_OBEY = False #可以忽略或者不遵守robots协议
# 不遵守robots协议
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
import scrapy

class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
# 最好注释这个允许域名 因为图片如果是其他域名的就获取不到了
# allowed_domains = ['www.qiushibaike.com/text']

# 默认生成的协议是http的需要手动修改为 https
start_urls = ['https://www.qiushibaike.com/text/']

def parse(self, response):
# 推荐xpath(scrapy里集成了xpath解析接口)

# 每个段子的外层容器
div_list = response.xpath('//div[@id="content-left"]/div')

for div in div_list:
# xpath解析指定内容被存到一个Selector对象里
'''
author = div.xpath('./div/a[2]/h2/text()')
content = div.xpath('.//div[@class="content"]/span/text()')

print(author) # [<Selector xpath='./div/a[2]/h2/text()' data='\n好吃的焦糖饼干~\n'>]
# xpath返回的 不是直接的内容而是一个列表 列表里是一个selector对象
'''

# extract()方法 可以将selector里存储的数据值拿到
# author = div.xpath('./div/a[2]/h2/text()').extract()
# content = div.xpath('.//div[@class="content"]/span/text()').extract()
# print(author) # ['\n好吃的焦糖饼干~\n']
author = div.xpath('./div/a[2]/h2/text()').extract_first()
content = div.xpath('.//div[@class="content"]/span/text()').extract_first()
print(author) # \n好吃的焦糖饼干~\n

pass

执行命令

1
2
scrapy crawl 爬虫名称 :该种执行形式会显示执行的日志信息
scrapy crawl 爬虫名称 --nolog:该种执行形式不会显示执行的日志信息