文章目录
- 项目简介
- 一、创建项目
- 1、终端创建项目
- 2、修改配置
- 二、爬取列表数据
- 1、数据分析
- 2、模型建立
- 3、存储为 json 数据
- 4、存储为 mysql 数据
- 三、爬取列表下一页及所有数据
- 1、特征分析
- 2、编写方法
- 四、图片
- 1、添加图片保存地址
- 2、添加图片请求
- 3、添加图片管道
- 五、爬取详情
- 六、添加下载中间件
- 1、代理 USER_AGENT
- 2、IP 池 PROXIES
- 七、设置日志
- 1、设置日志级别
- 2、设置日志保存地址
项目简介
eleduck 电鸭 是一款远程工作的招聘交流网站。这里仅做学习使用。
一、创建项目
1、终端创建项目
$ scrapy startproject WebScrapy # 创建项目
$ tree
$ cd WebScrapy # 进入项目文件
$ scrapy genspider eleduck "" # 创建爬虫
$ tree
- 1
- 2
- 3
- 4
- 5
- 6
- 7
# 检查爬虫
$ scrapy check eleduck # 此处根据爬虫的名字来区分,而非文件名
----------------------------------------------------------------------
Ran 0 contracts in 0.000s
OK
# 运行爬虫
$ scrapy crawl eleduck
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
2、修改配置
在 中:
# 1 修改 USER_AGENT,防止被反扒
USER_AGENT = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50'
# 2、设置不遵守 robots 协议
ROBOTSTXT_OBEY = False
- 1
- 2
- 3
- 4
- 5
二、爬取列表数据
1、数据分析
列表中包含 标题,发言人 等信息,可通过 xpath 找到
2、模型建立
在 已自动的创建的 WebscrapyItem 类下添加字段
class WebscrapyItem(scrapy.Item):
# define the fields for your item here like:
writer = scrapy.Field() # 作者
title = scrapy.Field() # 招聘标题
detailUrl = scrapy.Field() # 详情地址
iconUrl = scrapy.Field() # 头像 icon 地址
- 1
- 2
- 3
- 4
- 5
- 6
3、存储为 json 数据
在 中添加存储方法
import json
class WebscrapyPipeline:
def __init__(self):
self.f = open('','w')
def process_item(self, item, spider):
# print('writer : ', item['writer'])
# print(dict(item))
content = json.dumps(dict(item), ensure_ascii=False) + ',\n'
# num = \
self.f.write(content)
# print('num : ', num)
return item
def close_spider(self, spider):
self.f.close()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
4、存储为 mysql 数据
import pymysql
# 保存到 mysql
class WebscrapyPipeline:
def __init__(self):
self.conn = pymysql.connect("localhost", "root", "123456Aa*", "uinfo")
self.cursor = self.conn.cursor()
sql = '''
CREATE TABLE IF NOT EXISTS ed_list(
id INT PRIMARY KEY AUTO_INCREMENT,
writer VARCHAR(100),
title VARCHAR(100),
detailUrl VARCHAR(100) )
'''
ret = self.cursor.execute(sql)
print('创建数据表 : ', ret)
def process_item(self, item, spider):
sql = """ INSERT INTO ed_list(writer, title, detailUrl) \
VALUES ('%s', '%s', '%s')""" \
% (item['writer'], item['title'], item['detailUrl'])
# print(sql)
try:
# 执行sql语句
self.cursor.execute(sql)
# 执行sql语句
self.conn.commit()
print('插入数据 执行成功')
except:
# 发生错误时回滚
self.conn.rollback()
print('插入数据 执行失败')
return item
def close_spider(self, spider):
self.conn.close()
- 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
三、爬取列表下一页及所有数据
1、特征分析
当有下一页的时候,下一页这个 li 的 aria-disabled
属性为 false;最后一页时,这个属性为 true;
且下一级的 a 标签 href 属性有连接信息;拼接本站域名,刚好是下一页的url,如 /?page=96
2、编写方法
在 的 parse 方法中添加如下语句,判断是否有下一页,以及下一页的 url;
并发送下一页请求进行爬取。
# 检查列表是否有下一页
existNext = htmlTree.xpath('//li[@title="下一页"]/@aria-disabled')[0] # false
existNext_str = str(existNext)
print('existNext' ,existNext)
if existNext_str == 'false': # 有下一页,继续请求
next_page = htmlTree.xpath('//li[@title="下一页"]/a/@href')[0] # /?page=95
print('=' * 30, '\n', next_page)
url = urlPrefix + next_page
yield scrapy.Request(url, callback= self.parse) # 发送请求,并由本 parse 方法继续处理
else:
print('✔️' * 20,' 不存在下一页,爬取结束 ')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
四、图片
1、添加图片保存地址
在 中添加图片保存地址字段
# IMAGES_STORE = '/Users/user/Desktop/008/' # 绝对地址
IMAGES_STORE = 'rsc/pics/' # 相对地址,图片将位于 位于主目录的 rsc/pics/full 中
- 1
- 2
2、添加图片请求
在 中添加
IconPipeline
类,处理得到的 item,并发送请求
继承自 ImagesPipeline
,重写 get_media_requests
处理
重写 item_completed
方法,处理图片下载完成
import os
from WebScrapy.settings import IMAGES_STORE as imges_store
from scrapy.pipelines.images import ImagesPipeline
class IconPipeline(ImagesPipeline):
def get_media_requests(self, item, info):
print('\n-- IconPipeline process_item', item)
iconUrl = item['iconUrl']
yield scrapy.Request(iconUrl)
# 下载完成,重命名
def item_completed(self, results, item, info):
print('\n-- item_completed, results : ', results, ' info : ', info )
'''
-- item_completed, results : [(True, {'url': '/eleduck/avatar/!64', 'path': 'full/', 'checksum': '41cf3ebf2bf417f5957e642176565b37', 'status': 'downloaded'})]
info : < object at 0x7fd416c4c390>
'''
dict = [x for ok, x in results if ok][0]
print('\n-- dict : ', dict)
path = dict['path']
print('-- path : ', path)
originPath = images_store + path
print('-- originPath : ', originPath)
writer = item['writer']
targetPath = images_store + writer + '.jpg'
os.rename(originPath, targetPath)
return item
- 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
3、添加图片管道
将 IconPipeline 添加到 settings 的 ITEM_PIPELINES
中
ITEM_PIPELINES = {
'': 300,
'': 300,
}
- 1
- 2
- 3
- 4
运行爬虫后,会得到结果:
五、爬取详情
在列表中可以抓取到详情的地址,即 detailUrl 保存的内容;拼接本站url地址即详情页;
所以在获取 detailUrl 的时候,直接发出请求,让另一个parse 方法来处理即可;
本次详情数据直接保存到了 html 文件中。
修改 文件:
urlPrefix = ''
class EleduckSpider(scrapy.Spider):
...
def parse(self, response):
for node in list:
...
detailUrl = subnode.xpath('h2/a/@href')[0]
...
yield scrapy.Request(urlPrefix+detailUrl, callback=self.parseDetail) # 发送详情请求
yield item
# 处理详情
def parseDetail(self, response):
print('\n-- parseDetail : ', response.url, type(response.url))
arr = str(response.url).split('/')
print(arr)
# print(arr(-1)
path = 'rsc/details/' + arr[-1] + '.html'
print('-- detailFilePath : ', path)
html = response.text
with open(path, 'w') as f:
f.write(html)
print('详情保存成功:', path)
- 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
六、添加下载中间件
1、代理 USER_AGENT
1) 中添加 USER_AGENTS
键值对
USER_AGENTS = ['Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1',
'User-Agent:Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11',
'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11']
- 1
- 2
- 3
- 4
2)在 中添加 下载中间件类
创建类的时候,不能确定是 下载中间件 还是 中间件,由下一步在 setting 中配置决定。
实现 下载中间件的核心方法
import random
class DBDownloaderMiddleware:
def process_request(self, request, spider):
ua = random.choice(spider.settings['USER_AGENTS'])
request.headers['User-Agent'] = ua
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
3)将 DBDownloaderMiddleware
添加到 的
DOWNLOADER_MIDDLEWARES
键中
DOWNLOADER_MIDDLEWARES = {
# '': 543,
'': 544
}
- 1
- 2
- 3
- 4
4)在返回中检测 ua 是否设置成功
可以在 中添加 下载中间件类,检测返回的内容
# 检查 ua
class CheckRespDownloaderMiddleware:
def process_response(self, request, response, spider):
print(request.headers['User-Agent'])
return response
- 1
- 2
- 3
- 4
- 5
- 6
同样需要添加到 的
DOWNLOADER_MIDDLEWARES
键中
DOWNLOADER_MIDDLEWARES = {
# '': 543,
'': 544,
'': 545
}
- 1
- 2
- 3
- 4
- 5
2、IP 池 PROXIES
方法和上面雷同
1)中添加 PROXIES
键值对
PROXIES=[
{'ip_port':'http://171.35.12.213:9999' },
{'ip_port':'http://171.35.12.212:9999', "user_password":"username:password"},
{'ip_port':'http://171.35.12.22:9999', "user_password":"root:admin123456"},
]
- 1
- 2
- 3
- 4
- 5
2)在上述下载中间件类 DBDownloaderMiddleware
中添加代码
代码变为
class DBDownloaderMiddleware:
def process_request(self, request, spider):
# 设置 user-agent
ua = random.choice(spider.settings['USER_AGENTS'])
request.headers['User-Agent'] = ua
# 设置 proxy
proxy = random.choice(spider.settings['PROXIES'])
if proxy['user_password'] is None:
request.meta['proxy'] = proxy['ip_port']
else:
upwd = base64.b64encode(proxy['user_password'])
# 令牌
request.headers['Proxy-Authorization'] = 'Basic '+ upwd
request.meta['proxy'] = proxy['ip_port']
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
七、设置日志
1、设置日志级别
在 中添加
LOG_LEVEL
键值对,表明日志级别
LOG_LEVEL = 'WARNING'
- 1
Python 的内置日志记录定义了5个不同的级别来指示给定日志消息的严重性。以下是标准的,按降序排列:
DEBUG - 调试信息
INFO - 一般信息
WARNING - 警告信息
ERROR - 一般错误
CRITICAL - 严重错误
2、设置日志保存地址
在 中添加
LOG_LEVEL
键值对,表明日志记录文件的地址
LOG_FILE=''
- 1