絮叨一下(本言论参考其他作者)
boos直聘,想必对于找工作的同志都非常熟悉,为了快速获取boss上的发布职位信息
今天就用scrapy框架进行岗位,薪资,待遇,公司,招聘要求 等信息进行爬取
之前尝试单独使用scrapy进行爬取,直接在web上登录boss,从浏览器截取cookie,遇到了boss的各种反扒机制
为了解决这些头疼的反扒,在scrapy爬虫框架的基础上融入了django与selenium
你有可能会问,为什么要用django,scrapy框架就是一款极其简单的爬虫而已,没有封装ORM,对于大多数学习过Python的人来说,web框架中接触的最多的就是Django了,而Django自带的ORM非常好用,有ORM的帮助,爬下来的数据插入数据库非常的方便
不要认为scrapy提供的导入json文件或者把爬到的数据写入xsl表格就可以,因为在爬虫调试阶段存在各种试错与添加if判断,加上boss的反扒监测非常严格,既然启动了爬虫获得了数据,就不要浪费了,记录到数据库中,下次调试获取到新数据的时候只需要update新字段就行,如果觉得自己的SQL原生语句能力很强,那么请忽略我这段话继续往下看
分析
boss直聘网站: 「北京招聘信息」北京招聘网 - BOSS直聘
他的反爬还是很讨厌的,信息都是用cookies渲染生成的,cookies时效很短,很快就失效了,快速访问还会封掉你的ip ,使用代理会提示ip异常,最终一样逃不过人机交互接入码校验平台了,所以在这里我尝试过各种办法后,在爬虫中加入selenium来提高爬虫的稳定性
当然了首页是没有反爬的,那就慢一点,虽然这一点都不像爬虫的正确姿势,为了能稳定获取数据,也要慢慢来
思路简介
爬虫的核心还是要靠scrapy,因此django只是提供一个保存数据库的方法(django中叫做model),所以我们需要做的就是
在django上搭建一个model,让所有需要保存的数据通过这个model,依靠django框架完成保存
在scrapy上写一个爬虫,得到数据源,然后传递到这个model中
而selenium帮助你解决boss直聘更新cookie出现302跳转的问题,用scrapy中的middlewares中间件来截获selenium产生的response对象
大致的流程如下:
scrapy启动-->打开selenium-->创建浏览器对象-->打开浏览器载入scrapy中的第一个爬虫url-->selenium通过浏览器获取到boss直聘响应的response信息-->scrapy中的middlewares中间件截获response对象进行数据处理-->通过代码xpath拿到response对象中的数据-->数据关联django中的model模型完成数据的入库操作
开撸(环境是Python3.7.3 win64位 ,开发工具Pycharm2020.2版)
需要准备如下插件:
1、scrapy
2、django
3、selenium
4、pymysql
5、mysqlclient
6、scrapy-djangoitem
由于本人使用Pycharm2020.2(2020以上版本的IDE都支持中文)开发,所以对于环境安装非常便捷,这里不过于啰嗦环境的安装,所有的环境都在windows上安装,因为有使用到selenium,不推荐服务器版linux,有图形化界面的linux也行,因为boss一旦给你跳出人机校验的话,这里还是需要人工去解决以下的,目前暂时没有找到代码的处理方案
在Pycharm中安装插件包的方式如下:
以上插件都准备妥当之后
开始在Django上进行操作(先操作Django还是先操作Scrapy都无所谓,新手建议跟着我的步骤来,熟手可以无视步骤)
这里建议新建一个空文件夹,方便一步步熟悉整个流程,同时其他框架的文件也都在这个目录下。这里我新建了一个空文件夹:Scrapy_Boss
文件夹建立好了之后,在IDE的最下方点击“终端”,在终端的界面中输入以下命令来完成Django项目的创建操作(我创建的django项目名称为djangoboss):
1.在命令行中cd到这个空文件下,然后:
django-admin startproject djangoboss
如果是pycharm的编辑器,把Scrapy_Boss下的djangoboss文件夹标记为资源文件:(右键,下面有一个“标记目录为” 然后选“源 根”),文件夹为蓝色的就对了
这样就新建了一个django的项目,名字是djangoboss。总的来说在django上只需要完成两部操作:修改django中的settings文件来链接mysql数据库,同时建立一个app用来创建models模型
2、配置mysql
在这个位置找到settings.py文件,打开:
然后找到这段话,将这段话给注释掉(django默认配置sqlite数据库、sqlite是一种轻量级的微型数据库,多数在手机中使用,浏览器默认支持sqlite3):
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
替换掉,具体的配置根据自己的情况来:
DATABASES= {
'default': {
'ENGINE': 'django.db.backends.mysql', #django的数据库插件地址
'NAME': 'zhpdb_test', #数据库的名称
'HOST': '127.0.0.1', #数据库的地址
'PORT': 3306, #数据库端口
'USER': 'root', #数据库登录用户名
'PASSWORD': '123456', #数据库密码
}
}
此时完成Scrapy项目整体的30%配置,完成度django部分 50%
3、新建model
想要新建model,需要先建立一个app,然后依靠app来建model:
- 新建 app
1.首先进入这个项目中,命令需要使用manage.py,所以进入到manage.py同级的目录:
cd djangoboss/
2.然后建立app:为了便于区分,我叫它 savebossdate,用以表示是做存储的app:
python manage.py startapp savebossdate
3.在刚才的setting文件中找到这一栏,末尾添加‘savebossdate’:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'savebossdate', #将你新创建的项目载入到INSTALLED_APPS中,使得savebossdate项目在django中生效
]
4.到这里新建app的操作已经完成
- 新建model
1.打开savebossdate的文件夹,找到models.py文件并打开
添加如下代码:
# models.py
from django.db import models
# Create your models here.
class HrRecruitmentCatch(models.Model):
id = models.CharField(primary_key=True, max_length=40)
pk_id = models.CharField(max_length=50, blank=True, null=True)
region = models.CharField(max_length=100, blank=True, null=True)
experience = models.CharField(max_length=50, blank=True, null=True)
education = models.CharField(max_length=50, blank=True, null=True)
person_sum = models.CharField(max_length=10, blank=True, null=True)
hiring_time = models.CharField(max_length=50, blank=True, null=True)
position_info = models.CharField(max_length=100, blank=True, null=True)
address = models.CharField(max_length=500, blank=True, null=True)
salary = models.CharField(max_length=100, blank=True, null=True)
company_name = models.CharField(max_length=200, blank=True, null=True)
company_desc = models.TextField(blank=True, null=True)
job_description = models.TextField(blank=True, null=True)
company_address = models.CharField(max_length=200, blank=True, null=True)
source = models.CharField(max_length=20, blank=True, null=True)
source_keyword = models.CharField(max_length=20, blank=True, null=True)
url = models.CharField(max_length=500, blank=True, null=True)
job_url = models.CharField(max_length=500, blank=True, null=True)
create_time = models.DateTimeField(blank=True, null=True)
create_by = models.CharField(max_length=20, blank=True, null=True)
update_time = models.DateTimeField(blank=True, null=True)
update_by = models.CharField(max_length=20, blank=True, null=True)
company_logo = models.CharField(max_length=300, blank=True, null=True)
company_tags = models.CharField(max_length=300, blank=True, null=True)
job_vline = models.CharField(max_length=100, blank=True, null=True)
class Meta:
managed = False
db_table = 'hr_recruitment_catch'
此models中的字段信息是与数据库表一一对应的,如果对django很熟悉的童鞋可以用manage.py里的makemigrations命令来完成数据库迁移【这里的迁移是生成对应的表】,不熟悉的童鞋可以用建表语句去数据库创建,建表语句下面给出来
2.在当前目录(目录文件中包含有manage.py)的命令行中,输入如下命令,完成数据库新建超出:
python manage.py makemigrations
python manage.py migrate
与上面models对应的建表语句(数据库是mysql)
/*
Navicat Premium Data Transfer
Source Server : 123.57.55.99
Source Server Type : MySQL
Source Server Version : 50730
Source Host : 123.57.55.99:3306
Source Schema : zhpdb_test
Target Server Type : MySQL
Target Server Version : 50730
File Encoding : 65001
Date: 06/11/2020 17:30:32
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for hr_recruitment_catch
-- ----------------------------
DROP TABLE IF EXISTS `hr_recruitment_catch`;
CREATE TABLE `hr_recruitment_catch` (
`id` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键',
`pk_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '抓取数据唯一标识',
`region` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '招聘地区',
`experience` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '经验',
`education` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学历',
`person_sum` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '招聘人数',
`hiring_time` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '发布时间',
`position_info` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '职位',
`address` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址',
`salary` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '薪资范围',
`company_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '公司名称',
`company_desc` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '公司介绍',
`job_description` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '职位要求',
`company_address` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '公司地址',
`source` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '来源',
`source_keyword` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '搜索关键字',
`url` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '公司网站地址',
`job_url` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '招聘说明网站地址',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`create_by` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`update_by` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '更新人',
`company_logo` varchar(300) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '公司Logo',
`company_tags` varchar(300) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '技能要求',
`job_vline` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '招聘年限',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `recruitment_catch_index1`(`id`) USING BTREE,
INDEX `recruitment_catch_index2`(`pk_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
完成以上操作后,django这部分就结束了,之后就是scrapy的操作了。
在scrapy上的操作
- scrapy上总的来说有五步:
1、item.py编写,把django中的models延伸到scrapy中来
2、pipeline.py编写,告诉django保存item中的数据
3、settings.py配置,使整个scrapy爬虫框架生效
4、爬虫类编写,获取数据源
5、使我们可以在编辑器中调试这个整合过的项目
首先创建scrapy项目
首先我们需要先建立一个爬虫项目,通过pycharm底部的终端窗口,将命令行所在的位置移动到Scrapy_Boss这个根目录下,执行下面的命令就可以了:
cd .. # 进入到Scrapy_Boss的目录下,使得创建的scrapy项目与django是平级关系
scrapy startproject boss # 创建爬虫项目,名字是:boss
然后把最外层的boss标记为“源 根”(如果是pycharm)
连续点开两个boss文件后,我们可以看到这样子的目录:
- 1、items的编写
点开这个items.py文件,可以看到有一个BossItem(类文件)的模板
我们修改这个模板,为(修改后,可以通过from的导入路径发现HrRecruitmentCatch来自于Django中的models,同时让BossItem类继承DjangoItem,这样就强制的将scrapy与Django的ORM模型进行了关联,具体用法往后看):
import scrapy
from scrapy_djangoitem import DjangoItem #类文件的继承
from savebossdate.models import HrRecruitmentCatch #关联django中的models
class BossItem(DjangoItem):
django_model = HrRecruitmentCatch #HrRecruitmentCatch 是django中的models
这里,我们更换了继承的类,同时通过这个scrapy的items.py导入了django中的app的model,完成了两个框架间的关联
- 2、pipeline.py编写,告诉django保存item中的数据(使用django来保存数据,没有django如何保存数据,看2.1)
只需要添加一句话就行,在原有的BossPipeline的基础上改为(只需要一个item.save()就可以了):
class BossPipeline(object):
def process_item(self, item, spider):
item.save()
return item
- 2.1、在没有django的情况下scrapy保存数据用以下方式修改(没有django就需要新增一个“init”函数来建立SQL的链接,同时在process_item函数中手写SQL语句来完成数据的写入,因为process_item函数中的item参数是一个字典,里面有获取到的页面数据,保存方式可以有很多种,用IO写入到硬盘,或者写成cvs,写成xsl表格都是能记录数据的方式,本人喜欢SQL记录):
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# useful for handling different item types with a single interface
import logging
import pymysql
from itemadapter import ItemAdapter
from savebossdate.models import HrRecruitmentCatch
class BossPipeline(object):
# 创建init,使得方法运行时初始化mysql的链接
def __init__(self):
# 连接数据库
self.connect = pymysql.connect(
host='127.0.0.1', # 数据库地址
port=3306, # 数据库端口
db='zhpdb_test', # 数据库名
user='root', # 数据库用户名
passwd='123456', # 数据库密码
charset='utf8', # 编码方式
use_unicode=True)
# 通过cursor执行增删查改
self.cursor = self.connect.cursor()
def process_item(self, item, spider):
# pk_id = item['pk_id']
# source_keyword = item['source_keyword']
# pk_id_obj = HrRecruitmentCatch.objects.filter(pk_id=pk_id, source_keyword=source_keyword)
# if not pk_id_obj:
# item.save()
# else:
# pk_id_obj.update(**item)
company_uuid = str(uuid.uuid1().hex)
pk_id = item['company_pkid']
company_name = item['company_name']
region = item['job_area']
position_info = item['job_name']
company_logo = item['company_icon']
company_tags = item['company_tags']
hiring_time = item['job_pub_time']
salary = item['job_limit']
job_vline = item['job_vline']
url = item['url']
sql_inset = 'INSERT INTO hr_recruitment_catch(id,pk_id,company_name,region,position_info,company_logo' \
',company_tags,hiring_time,salary,job_vline,url,source) ' \
'VALUES ("{}","{}","{}","{}","{}","{}","{}","{}","{}","{}","{}","{}"' \
')'.format(company_uuid, pk_id, company_name, region, position_info, company_logo
, company_tags, hiring_time, salary, job_vline, url, 'BOSS直聘')
print(sql_inset)
try:
# 执行sql语句
self.cursor.execute(sql_inset)
# 提交到数据库执行
self.connect.commit()
print('提交了数据')
except:
print('发生了错误')
# 如果发生错误则回滚
self.connect.rollback()
return item
- 3、settings.py配置,使整scrapy完整,基本可以运行
- 第一个:
ROBOTSTXT_OBEY = False # 这个是一个机器人标识,请关掉它
- 第二个:默认会被注释掉,只需要取消注释即可
ITEM_PIPELINES = {
'boss.pipelines.BossPipeline': 300,
}
- 第三个,在settings.py文件的最上方添加以下代码,目的是为了让scrap运行的同时去找到django,并把django给启动,django的models是需要django运行才能正常使用的
import datetime
import os
import sys
import django
BASE_DIR = os.path.dirname(os.path.abspath(os.path.dirname(__file__)))
PRO_ROOT = os.path.dirname(BASE_DIR) # 两个项目共同的根目录
sys.path.append(os.path.join(PRO_ROOT, 'boss'))
sys.path.append(os.path.join(BASE_DIR, 'boss'))
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangoboss.settings'
django.setup()
- 第四个,爬虫类编写,获取数据源
首先建立一个爬虫,进入到终端,用以下命令:
scrapy genspider startboss 'www.zhipin.com'
此时你就可以在boss>boss的目录下找到一个刚刚生成的spiders文件夹,此文件夹下有一个startboss.py的文件,此文件用来编写爬虫的具体实现方式,具体代码已全部给出:
(代码具体说明,新手注意看)因为文章写到这整体爬虫项目是可以完整运行的,唯独缺少了Selenium,如果没有Selenium的情况下StartbossSpider类文件中必须配置以下1、2,如果配置了Selenium,那么cookie_list就变得无所谓了:
1、cookie_list
2、custom_settings
因为boss的反扒机制,所有的页面的数据会有类似防伪标志一样,防伪数据是写入到cookie里的,如果你问我cookie_list里的参数去哪找,看截图:
在文章的开头我就说过,boss直聘的反扒机制很恶心,我分析过之后boss的反扒机制是大致情况如下:
- 在固定频率的请求下boss直聘的服务器会检测你的请求,如果都是固定的请求时间,比如都是1秒请求一次,服务器会将你列为可疑对象,大概过一段时间IP直接拉黑
- 登陆自己的帐号之后可以防止服务器拉黑你的IP,但是服务器还是会把你列为可疑对象,此时boss直聘的服务器就利用cookie来测试你是否为爬虫,原来请求的一个A链接地址会突然出现一个302跳转,此时的一个跳转页面会将你当前的请求重定向去cookie更新的服务器获取一个叫zp_stoken参数,然后这个参数会每隔几秒或者几分钟失效,失效后又出现一个302跳转来更新zp_stoken参数的值,如果你是机器爬虫,肯定不好解决这个cookie写入的问题,之前有看一个反扒取大神找到了zp_stoken的生成js,通过反解析js提取到了生成方法ABC函数,利用Node.js来运行,自己手动注入新的cookie来完善爬虫
- 如果你真的以为找到ABC函数自己注入cookie就结束了吗?因为你的请求已经被boss直聘服务器监控到了,最终他们会在你的请求中302强制跳转一个人机交互验证码出来,是下面这个样子的,boss直聘用的空间推理验证码,出自GEETEST厂家非常恶心,代码是完全无法跳过这一层的,所以为了稳定爬取数据,我选择加入selenium来完成这个恶心的验证码,别以为机器能绕过,这个恶心的验证码一定是人工无法绕过的。
import random
import time
import uuid
import scrapy
from boss.items import BossItem
from boss.replacetool import replace_tool
class StartbossSpider(scrapy.Spider):
name = 'startboss'
allowed_domains = ['www.zhipin.com']
start_urls = ['https://www.zhipin.com/c101010100/?query=web&page=4&ka=page-4']
cookie_list = 'JSESSIONID=""; _bl_uid=4ykqjfX6hdkyydmCyw1Lp9t5pIn8; t=qh4neJeO0hJ6VRos; wt=qh4neJeO0hJ6VRos; lastCity=101010100; __zp_seo_uuid__=206a837f-b53b-4e88-9e8a-53313a08b64c; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1603241871,1604389355,1604568321,1604569983; __g=-; __c=1601020932; __l=l=%2Fwww.zhipin.com%2Fbeijing%2F&r=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DY0MdiRccX-4tmkpeDiIC0ijU-gd1YeFv87bTN880xDar_lR5pQIs1LjhXwPev-UA%26wd%3D%26eqid%3D876d94d600040234000000035fa3cb79&g=&friend_source=0&friend_source=0; __a=86720333.1601020932..1601020932.189.1.189.189; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1604570405; __zp_stoken__=c1a6baTQnfQ13ED8OPSBbMF01XmESdmlnPkdSbikDdjhAfz1jAzlkZXhjIQJnOU9GD3tWBEgoFgwid2Y1MmcpEEloaVIwW0k%2FSB54J2ogC0oiQSZJQ3MISlFiOSwOTEQcCSpcFwdWDAZdfnhWOQ%3D%3D; __zp_sseed__=HXp3GPvGQtLqMBAdoUyHHD7OOY8XbWzi5fpcYurmhA8=; __zp_sname__=c61cd8a7; __zp_sts__=1604570407347'
custom_settings = {
'DEFAULT_REQUEST_HEADERS': {
'Cookie': cookie_list,
'Referer': 'https://www.zhipin.com',
}
}
def parse(self, response):
print('*' * 40)
print(response.url)
# if not response.headers.getlist('Set-Cookie'):
# return
# 获取当前页面的列表数据
job_list = response.xpath('//div[@class="job-list"]/ul/li')
# print(job_list)
for i in job_list:
print('开始获取数据')
bossitem = BossItem()
bossitem['id'] = str(uuid.uuid1().hex)
company_url = i.xpath('./div/div/div[@class="info-company"]/div/h3/a/@href').get()
bossitem['url'] = 'https://www.zhipin.com{}'.format(company_url)
bossitem['pk_id'] = company_url.split('/')[2].split('.')[0]
pk_id = bossitem['pk_id']
url = bossitem['url']
bossitem['job_url'] = 'https://www.zhipin.com{}'.format(
i.xpath('./div/div[1]/div[1]/div/div[1]/span[1]/a/@href').get())
job_url = bossitem['job_url']
bossitem['company_name'] = i.xpath('./div/div/div[@class="info-company"]/div/h3/a/text()').get()
bossitem['company_logo'] = i.xpath('./div/div[1]/div[2]/a/img/@src').get()
tags = i.xpath('./div/div[2]/div[1]/span/text()').extract()
bossitem['company_tags'] = ','.join(tags)
bossitem['position_info'] = i.xpath(
'./div/div[1]/div[@class="primary-wrapper"]/div/div[@class="job-title"]/span[1]/a/text()').get()
bossitem['region'] = i.xpath('./div/div[1]/div[1]/div/div[1]/span[2]/span/text()').get()
bossitem['hiring_time'] = i.xpath('./div/div[1]/div[1]/div/div[1]/span[3]/text()').get()
bossitem['salary'] = i.xpath('./div/div[1]/div[1]/div/div[2]/span/text()').get()
job_limit = i.xpath('./div/div[1]/div[1]/div/div[2]/p/text()').extract()
bossitem['job_vline'] = job_limit[0]
bossitem['education'] = job_limit[1]
bossitem['source'] = 'BOSS直聘'
bossitem['source_keyword'] = 'web'
yield bossitem
time.sleep(2)
# yield scrapy.Request(url, callback=lambda response, pk_id=pk_id: self.boss_company_desc(response, pk_id))
yield scrapy.Request(job_url, callback=lambda response, pk_id=pk_id: self.boss_company_job(response, pk_id))
# company_url = response.xpath('//div[@class="job-list"]/ul/li/div/div/div[@class="info-company"]/div/h3')
# for i in company_url:
# url = 'https://www.zhipin.com' + i.xpath('./a/@href').get()
# print(url)
# pk_id = i.xpath('./a/@href').get().split('/')[2].split('.')[0]
# time.sleep(random.randint(1,5))
# yield scrapy.Request(url, cookies=self.cookie_list, callback=lambda response,pk_id=pk_id:self.boss_company_desc(response,pk_id))
# 定义下页标签的元素位置
next_page = response.xpath('//div[@class="page"]/a/@href').extract()[-1]
print('next_page--->', next_page)
# 判断什么时候下页没有任何数据
if next_page != 'javascript:;':
base_url = "https://www.zhipin.com"
url = base_url + next_page
time.sleep(random.randint(5,10))
yield scrapy.Request(url=url, cookies=self.cookie_list, callback=self.parse)
#采集公司信息
def boss_company_desc(self, response, pk_id):
bossitem = BossItem()
bossitem['pk_id'] = pk_id
bossitem['company_desc'] = response.xpath('//*[@id="main"]/div[3]/div[1]/div[1]/div/div[1]/div/text()').get()
yield bossitem
#采集公司发布的职位信息,发现这个页面也包含公司信息,所以注释掉了boss_company_desc函数的运行实体,上面的函数不会运行
def boss_company_job(self, response, pk_id):
bossitem = BossItem()
bossitem['pk_id'] = pk_id
bossitem['job_description'] = replace_tool(response.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div').get())
bossitem['company_desc'] = replace_tool(response.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[2]/div/text()').get())
bossitem['source_keyword'] = 'web'
yield bossitem
- 第五个、建立一个main文件,方便爬虫直接运行,无需命令运行,在boss>boss目录下新建main.py,在此文件中加入以下代码:
# from scrapy import cmdline
#
# cmdline.execute("scrapy crawl startboss".split())
import sys
import os
from scrapy.cmdline import execute
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
execute(["scrapy", "crawl", "startboss"]) # 这句代码会执行爬虫类中 name = "startboss"的类,也就是运行StartbossSpider类,使得爬虫正式运行
加入Selenium完善爬虫的稳定性
因为没有ABC的函数体,也不清楚boss是否有更新ABC函数,在无法解决cookie加密的问题与302重定向的问题,既然浏览器自己本身可以完成cookie的更新获取,那我只需要通过载入selenium加载浏览器帮我完成这一步就可以自动更新cookie了,废话不多说,开始继续写代码,文章的开头有让大家安装Selenium三方库
安装命令如下
pip install selenium
Selenium的运行是会在你的电脑上打开浏览器的,所以需要对应浏览器的驱动,去下面根据自己的浏览器版本下载驱动,推荐Chrome与Firefox,驱动下载:
windows:
chrome 全系列下载地址
Firefox 全系列下载地址
IE 全系列下载地址
对测试人员来说selenium非常的强大,selenium的具体使用方式参考全网超级详细的selenium介绍! 本文不对selenium做过多的介绍
根据你使用的浏览器下载好驱动之后,把驱动exe文件放入到boss>boss目录下,别放错目录:
打开middlewares调整中间件(拦截器)的代码,用于截取Selenium中的response
不要动middlewares.py中原来的代码,因为在scrapy的settings.py文件中我们还没有打开中间件的设置,所以我们只需要加入代码即可,打开middlewares加入以下代码:
class CookiesMiddlewares(object):
# 需要随机的请求头
USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36',
'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML like Gecko) Chrome/44.0.2403.155 Safari/537.36',
'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; pl-PL; rv:1.0.1) Gecko/20021111 Chimera/0.6',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418.8 (KHTML, like Gecko, Safari) Cheshire/1.0.UNOFFICIAL',
'Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.8.1b2) Gecko/20060821 BonEcho/2.0b2 (Debian-1.99+2.0b2+dfsg-1)'
]
def __init__(self):
print("初始化浏览器")
self.driver = webdriver.Chrome()
def process_request(self,request,spider):
# 随机生成一个请求头
user_agent = random.choice(self.USER_AGENTS)
request.headers['User-Agent'] = user_agent
self.driver.get(request.url)
time.sleep(5)
# 我们等待5秒钟,让其加载
source = self.driver.page_source
#获取页面的源码
response = HtmlResponse(url=self.driver.current_url,body=source,request=request,encoding='utf-8')
# Response 对象用来描述一个HTTP响应
return response
# 这样我们就获取到了所有的信息,并返回response
然后打开scrapy中的settings.py文件,修改配置使得middlewares中的类文件生效:
#修改下载延迟时间,DOWNLOAD_DELAY设置越大请求越慢
DOWNLOAD_DELAY = 3
# 修改并发请求数,修改为1,或者2,越小爬取速度越慢,太快容易被识别到
CONCURRENT_REQUESTS = 1
"""cookie的设置"""
COOKIES_ENABLED = False
"""开启中间建"""
DOWNLOADER_MIDDLEWARES = {
'boss.middlewares.CookiesMiddlewares': 543,
# 'boss.middlewares.UserAgentDownloadMiddleWare': 543,
}
做完以上的所有操作后整个的(Python版) Scrapy+Django+Selenium 爬取Boss直聘 职位信息到这里就算告一段落了
整体代码大部分都带有注释,新手使用注意看注释自己调整
写给看到最后的你
朋友,感谢你看到了最后,技术不成熟的地方可以给予评论,一同完善,原创不宜,请勿乱喷,作者深知高手在明间
同时写给看到最后的“你”
一路走来,风雨无阻,感谢你陪我一起深入python的世界进行探索,共同成长,希望这篇文章能够帮助你了解scrapy
所有的代码仅供大家练习Scrapy使用,如果有侵权现象,请联系作者第一时间删除本文