人生苦短,快学Python!
初学scrapy
之后,发现就是效率对比于selenium和requests快了很多,那么问题来了,如果网站设置了反爬,比如User-Agent反爬,cookie反爬,IP封禁等等,所以我们需要通过集成selenium到scrapy中,绕过网站反爬,达到目的。
这里选择智联招聘
网站作为案例,就是虽然不是动态网页,但是它需要模拟登录,所以我们通过scrapy集成selenium进行数据抓取。
一、需求分析
打开目标网站,搜索web前端开发工程师。
这是首页,由于我的当前位置在武汉,所以系统自动定位到武汉,点击搜索后:
这个就是需要通过selenium出路的一个点。
手动登录后得到以下界面:
我们的目标是每一条招聘信息的8条数据:
name职位名称
salary薪资
adress地区
experience经验
eduBack教育背景
company公司名称
companyType公司类型
scale公司规模
info简介
二、scrapy项目文件配置
定义items
importscrapyclassZlzpItem(scrapy.Item):name=scrapy.Field()***薪资公司规模...***info=scrapy.Field()
定义scrapy爬虫:zl.py(智联)
#这里先说明下url:firstPageUrl:'/?jl=736&kw=web%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88&p=1'#作为第一页的url,下面的myspider.py中就不在展示,避免代码冗余。base_url='/?jl=736&kw=web%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88&p={}'
然后下面是zl.py的源码:(分为几个部分)
1、初始化设置:
#-*-coding:utf-8-*-importscrapyfromzlzp.itemsimportZlzpItemcount=1#定义一个全局变量,与base_url构建下一页的urlclassZlSpider(scrapy.Spider):name='zl'allowed_domains=['']start_urls=[firstPageUrl]
2、parse函数:
defparse(self,response):globalcountcount+=1#每解析一次页面,让count+1,和baseurl构造下一页的urljobList=response.xpath('//div[@class="positionlist"]/div/a')forjobinjobList:name=job.xpath("./div[1]/div[1]/span[1]/text()").extract_first()...salary***,company***,....info=job.xpath("./div[3]/div[1]//text()").extract_first()item=ZlzpItem(name=name,salary=salary,company=company,adress=adress,experience=experience,eduBack=eduBack,companyType=companyType,scale=scale,info=info)yielditem
3、分页:
next_url='/?jl=736&kw=web%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88&p={}'.format(count)ifcount==34:returnNone#设置程序停止的条件ifnext_url:yieldscrapy.Request(next_url,callback=self.parse)
定义下载器中间件(DownloadMiddleware):myDownloadMiddleware.py
fromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.supportimportexpected_conditionsasEC...classZlzpDownloaderMiddleware:def__init__(self):self.driver=webdriver.Chrome()defprocess_request(self,request,spider):self.driver.get(request.url)time.sleep(3)#休息3s#设置显示等待,由于需要登录,我们手机扫码登录,知道页面出现(即url显示为firstpageurl)WebDriverWait(self.driver,1000).until(EC.url_contains(request.url))time.sleep(6)#登录成功之后页面需要时间加载出来,再休息几秒returnHtmlResponse(url=self.driver.current_url,body=self.driver.page_source,encoding="utf-8",request=request)#然后把这个response对象返回给爬虫(zl.py)
说明:
selenium集成到scrapy中的核心就是在爬虫中间件中拦截请求,把处理后的响应对象返回,对应于爬虫文件(这里的zl.py)parse函数中的response,如果不集成selenium,那么response对象不能很好应对网站的反爬.
此处的parse_request方法中只有少量的selenium代码,因为动态操作其实不多.
重点:return后面的response对象:
在这里我们不能return None,如果return None,那么请求会被发送到下载中间件去下载这个页面,在将这个页面的response返回给spider(hr.py)。但是我们上面browser.get的时候就已经下载了这个页面的内容,所以没有必要在下载一次,我们只要制定一个response对象,直接返回这个response给spider即可
定义管道(Pipeline):pipelines.py
fromitemadapterimportItemAdapterimportcsvclassZlzpPipeline:def__init__(self):self.f=open('zlJob.csv','w',encoding='utf-8',newline='')#self.file_name=['name','upTime','salary','needs','welfare','company','scale','types']self.file_name=['name','salary','company','adress','experience','eduBack','companyType','scale','info']self.writer=csv.DictWriter(self.f,fieldnames=self.file_name)self.writer.writeheader()defprocess_item(self,item,spider):self.writer.writerow(dict(item))#写入spider传过来的具体数值returnitem#写入完返回defclose_spider(self,spider):self.f.close()
settings.py配置
BOT_NAME='zlzp'SPIDER_MODULES=['zlzp.spiders']NEWSPIDER_MODULE='zlzp.spiders'LOG_LEVEL='WARNING'......ROBOTSTXT_OBEY=False......DEFAULT_REQUEST_HEADERS={'user-agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/91.0.4472.106Safari/537.36','Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language':'en',}......DOWNLOADER_MIDDLEWARES={'zlzp.middlewares.ZlzpDownloaderMiddleware':543,}......ITEM_PIPELINES={'zlzp.pipelines.ZlzpPipeline':300,}......
......表示注释代码,这里省略。
三、程序运行
命令行键入:
scrapycrawlhr
pic1:运行程序结束到第34页,对应count = 34
pic02:(csv文件)
四、数据简单分析
查看数据
importpandasaspddf=pd.read_csv('./zlJob.csv')df.head()
薪资饼图展示
c=(Pie(init_opts=opts.InitOpts(bg_color="white")).add("",[list(z)forzinzip(typesX,number)])#zip函数两个部分组合在一起list(zip(x,y))----->[(x,y)].set_global_opts(title_opts=opts.TitleOpts(title="类型:"))#标题.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{c}"))#数据标签设置)c.render_notebook()
经验要求柱图展示
frompyecharts.chartsimportBarbar=Bar()bar.add_xaxis(['3-5年','1-3年','不限','5-','无经验','1年以下','以上'])bar.add_yaxis('经验要求',[462,329,83,78,19,15,4])bar.render()
学历要求柱图展示
c=(Pie(init_opts=opts.InitOpts(bg_color="white")).add("",[list(z)forzinzip(educationTypes,number)])#zip函数两个部分组合在一起list(zip(x,y))----->[(x,y)].set_global_opts(title_opts=opts.TitleOpts(title="类型:"))#标题.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{c}"))#数据标签设置)c.render_notebook()
大多数要求本科学历,或者说大专及以上学历。
公司类型柱图展示
c=(Pie(init_opts=opts.InitOpts(bg_color="white")).add("",[list(z)forzinzip(companyTypes,number)])#zip函数两个部分组合在一起list(zip(x,y))----->[(x,y)].set_global_opts(title_opts=opts.TitleOpts(title="类型:"))#标题.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{c}"))#数据标签设置)c.render_notebook()
可以看到大多数公司是民营或者上市公司。
五、总结
页面翻页处理,由于我们只是使用selenium就是打开网页请求数据,所以一般在爬虫文件中进行翻页处理,如果对应的下一页的a标签的href属性不是下一页的页面url,我们需要设置动态全局变量,构建动态的url。
下载中间件中设置的selenium的相关操作,动态点击,页面滚轮操作,显隐式等待等等,重要的是返回的response对象,这个是集成selenimu到scrapy的核心,在下载中间件中拦截请求,把处理后的response对象返回给爬虫。
分享给更多朋友,转发,点赞,在看