爬虫 - Scrapy 爬取某招聘网站
文章目录
-
-
项目简介
-
一、创建项目
-
- 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 "https://eleduck.com" # 创建爬虫
$ tree
# 检查爬虫
$ scrapy check eleduck # 此处根据爬虫的名字来区分,而非文件名
----------------------------------------------------------------------
Ran 0 contracts in 0.000s
OK
# 运行爬虫
$ scrapy crawl eleduck
2、修改配置
在 setting.py 中:
# 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、数据分析

列表中包含 标题,发言人 等信息,可通过 xpath 找到
2、模型建立
在 item.py 已自动的创建的 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 地址
3、存储为 json 数据
在 pipelines.py 中添加存储方法
import json
class WebscrapyPipeline:
def __init__(self):
self.f = open('eleduck.json','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()
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、特征分析
当有下一页的时候,下一页这个 li 的 aria-disabled 属性为 false;最后一页时,这个属性为 true;
且下一级的 a 标签 href 属性有连接信息;拼接本站域名,刚好是下一页的url,如 https://eleduck.com/?page=96


2、编写方法
在 eleduck.py 的 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、添加图片保存地址
在 setting.py 中添加图片保存地址字段
# IMAGES_STORE = '/Users/user/Desktop/008/' # 绝对地址
IMAGES_STORE = 'rsc/pics/' # 相对地址,图片将位于 位于主目录的 rsc/pics/full 中
2、添加图片请求
在 pipelines.py 中添加 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': 'https://duckfiles.oss-cn-qingdao.aliyuncs.com/eleduck/avatar/7e70c1b0-269d-4e0b-a78e-07f762455c2a.png!64', 'path': 'full/9add2ac38247cbe97714469ab2134ac1b83e8e28.jpg', 'checksum': '41cf3ebf2bf417f5957e642176565b37', 'status': 'downloaded'})]
info : <scrapy.pipelines.media.MediaPipeline.SpiderInfo 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
3、添加图片管道
将 IconPipeline 添加到 settings 的 ITEM_PIPELINES 中
ITEM_PIPELINES = {
'WebScrapy.pipelines.WebscrapyPipeline': 300,
'WebScrapy.pipelines.IconPipeline': 300,
}
运行爬虫后,会得到结果:

五、爬取详情
在列表中可以抓取到详情的地址,即 detailUrl 保存的内容;拼接本站url地址即详情页;
所以在获取 detailUrl 的时候,直接发出请求,让另一个parse 方法来处理即可;
本次详情数据直接保存到了 html 文件中。
修改 eleduck.py 文件:
urlPrefix = 'https://eleduck.com'
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、代理 USER_AGENT
1)Settings.py 中添加 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']
2)在 middlewares.py 中添加 下载中间件类
创建类的时候,不能确定是 下载中间件 还是 中间件,由下一步在 setting 中配置决定。
实现 下载中间件的核心方法
import random
class DBDownloaderMiddleware:
def process_request(self, request, spider):
ua = random.choice(spider.settings['USER_AGENTS'])
request.headers['User-Agent'] = ua
3)将 DBDownloaderMiddleware 添加到 settings.py 的 DOWNLOADER_MIDDLEWARES 键中
DOWNLOADER_MIDDLEWARES = {
# 'WebScrapy.middlewares.WebscrapyDownloaderMiddleware': 543,
'WebScrapy.middlewares.DBDownloaderMiddleware': 544
}
4)在返回中检测 ua 是否设置成功
可以在 middlewares.py 中添加 下载中间件类,检测返回的内容
# 检查 ua
class CheckRespDownloaderMiddleware:
def process_response(self, request, response, spider):
print(request.headers['User-Agent'])
return response
同样需要添加到 settings.py 的 DOWNLOADER_MIDDLEWARES 键中
DOWNLOADER_MIDDLEWARES = {
# 'WebScrapy.middlewares.WebscrapyDownloaderMiddleware': 543,
'WebScrapy.middlewares.DBDownloaderMiddleware': 544,
'WebScrapy.middlewares.CheckRespDownloaderMiddleware': 545
}
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"},
]
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、设置日志级别
在 settings.py 中添加 LOG_LEVEL 键值对,表明日志级别
LOG_LEVEL = 'WARNING'
Python 的内置日志记录定义了5个不同的级别来指示给定日志消息的严重性。以下是标准的,按降序排列:
DEBUG - 调试信息
INFO - 一般信息
WARNING - 警告信息
ERROR - 一般错误
CRITICAL - 严重错误
2、设置日志保存地址
在 settings.py 中添加 LOG_LEVEL 键值对,表明日志记录文件的地址
LOG_FILE='ed.log'
