本次主要内容是分享下拉勾网站模拟搜索以及搜索内容的爬取,这里先引入一些用到的库,由于网站本身的反爬虫技术和网络原因,这里使用了fake_useragent和多线程模式,当然如果有条件的话也可以使用代理池,这样可以更加保险一点。由于我没有弄那些收费的代理,而免费的代理有时会出现问题,所有就没有使用。
import requests
import json
import pymongo
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
import time
from requests.exceptions import RequestException
import threading
from queue import Queue
首先通过分析网页network工作情况可知,拉勾的搜索页面是运用的ajax技术,所以首先要找到数据接口网址,作为post数据的初始网址,然后分析所带的参数,通过分析可以first的值和pn键的值对应的关系,可以使用一个判断语句控制,内容如下:
url="https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false"
tag = "true" if pn == 1 else "false"
data = {
'first': true,
'pn': 1,
'kd':python
}
然后就是在返回的包中提取内容,通过分析包可以知道搜索返回的数据,找到需要的数据。这里由于我们需要进入每个搜索内容的详情页,所以我们需要获得搜索内容返回的页面的id,然后进行字符串操作获得详情页的真实网址:
results=page['content']['positionResult']['result']
ret=[]
for result in results:
id=result['positionId']
ret.append(id)
urls = ['https://www.lagou.com/jobs/{}.html'.format(i) for i in ret]
最后就是进入详情页的解析并存入数据库就行了。
具体的代码如下 :
import requests
import json
import pymongo
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
import time
import pymongo
from config import * #引入mongodb的配置文件
from requests.exceptions import RequestException
from mongocache import MongoCache #这个引入的是我自己写的一个mongodb存储的小程序,下面会写
#最简单的方法是直接使用pymongo进行存储操作,如下面的save_to_mongo
import threading
from queue import Queue
client=pymongo.MongoClient(MONGO_URL,connect=False)
db=client[MONGO_DB]
mongo=MongoCache()
def save_to_mongo(result): #数据存储方法
try:
if db[MONGO_DB].insert(result):
print('成功存入')
except Exception:
print("失败")
class ShiShi:
def __init__(self):
_ua=UserAgent() #模拟随机生成useragent
#cookie是我摘取的我自己的已登录的cookie,这个根据自己进行更改
self.headers={
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'User-Agent':_ua.random,
'Accept': '*/*',
# 'Accept-Language': '',
'Connection': 'keep-alive',
'Host': 'www.lagou.com',
'Referer': 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=',
'Cookie': 'cookie',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
}
self.url="https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false"
self.q = Queue(10)
#发送带参数的post请求
def get_page(self,pn,kd):
tag = "true" if pn == 1 else "false"
data = {
'first': tag,
'pn': pn,
'kd':kd
}
page=requests.post(self.url,headers=self.headers,data=data).json()
return page
#获取页面id,并存入到空列表中
def get_id(self,page):
results=page['content']['positionResult']['result']
ret=[]
for result in results:
id=result['positionId']
ret.append(id)
return ret
#对搜索返回的不同页面的id进行整合
def get_mid(self,pn,kd):
list = []
for i in range(1, int(pn) + 1):
page = self.get_page(i, kd)
ids = self.get_id(page)
list.extend(ids)
return list
#对id拼接成详情页的url
def get_real_url(self,ret):
urls = ['https://www.lagou.com/jobs/{}.html'.format(i) for i in ret]
return urls
#对详情页进行解析并存入到数据库
def get_page_detail(self,url):
try:
response=requests.get(url,headers=self.headers)
time.sleep(10)
if response.status_code==200:
soup=BeautifulSoup(response.text,'lxml')
conpany_name=soup.select('#job_company > dt > a > img')
work_name=soup.select('body > div.position-head > div > div.position-content-l > div > span')
salary=soup.select('body > div.position-head > div > div.position-content-l > dd > p:nth-of-type(1) > span.salary')
work_place=soup.select('body > div.position-head > div > div.position-content-l > dd > p:nth-of-type(1) > span:nth-of-type(2)')
exp_demand=soup.select("body > div.position-head > div > div.position-content-l > dd > p:nth-of-type(1) > span:nth-of-type(3)")
edu_demand=soup.select('body > div.position-head > div > div.position-content-l > dd > p:nth-of-type(1) > span:nth-of-type(4)')
job_desc=soup.select('#job_detail > dd.job_bt > div')
for 公司名称,工作名称,工资待遇,工作地点,经验要求,学历要求,工作描述 in zip(conpany_name,work_name,salary,work_place,exp_demand,edu_demand,job_desc):
data={
"公司名称" : 公司名称.get('alt'),
"工作名称": 工作名称.get_text(),
"工资待遇":工资待遇.get_text(),
"工作点点":工作地点.get_text(),
"经验要求":经验要求.get_text(),
"学历要求":学历要求.get_text(),
"工作描述":工作描述.get_text(),
}
mongo.__setitem__(url,data)
# write_to_file(data)
else:
print("无法连接")
except RequestException:
print('出现错误')
#多线程的生产者模式函数
def produce(self):
pn = input('请输入你想要爬取多少页:')
kd = input('你就说你想要搜啥:')
index=0
list=self.get_mid(pn,kd)
urls=self.get_real_url(list)
while True:
if index < len(urls):
self.q.put(urls[index])
index+=1
#消费者函数
def consume(self):
while True:
url=self.q.get()
self.get_page_detail(url)
print('thread is {} content is {}'.format(threading.current_thread(),url))
def main(self):
p1 = threading.Thread(target=self.produce, )
p2 = threading.Thread(target=self.consume, )
p3 = threading.Thread(target=self.consume, )
p4 = threading.Thread(target=self.consume, )
p5 = threading.Thread(target=self.consume, )
p6 = threading.Thread(target=self.consume, )
p7 = threading.Thread(target=self.consume, )
p1.start()
p2.start()
p3.start()
p4.start()
p5.start()
p6.start()
p7.start()
if __name__ == '__main__':
haha=ShiShi()
haha.main()
这只是一个简单的聚焦爬虫,其实对于这类含有ajax的网站来说,使用selenium能更加快捷有效的进行爬取。