Python

python爬虫07-分析ajax爬取拉勾网职位(一)

2019-03-28  本文已影响8人  DKider

想吃石锅鱼,我现在还没吃上饭。。。。

拉勾网我想爬好久了,但是苦于不会分析ajax,搁了挺久,现在学会了,终于可以如愿所偿了。

虽然说爬虫已经完成了,但还是有很多不足,比如,只能爬取120条信息,也就是9页,之后我的ip就会被封掉。
下面几点问题:

第一点我可以利用requests来更新cookies,但是现在还不会;第二点可以利用代理池解决但是我现在也不。这就是为什么我要把这个分开来写的原因。
先来看看我现在能够做到哪一步:
利用到的库:

import requests
import json
from pymongo import MongoClient
from multiprocessing import Pool
from urllib.parse import urlencode
import time

一定要安装数据库,并且打开服务,我用的mongodb(真好用),你们用别的也行。
我看了看拉勾网的robots.txt文件,如下:

User-agent: Jobuispider
Disallow:  /

User-agent: *
Disallow: /resume/
Disallow: /nearBy/
Disallow: /ologin/
Disallow: /jobs/list_*
Disallow: /one.lagou.com
Disallow: /ns3.lagou.com
Disallow: /hr.lagou.com
Disallow: /two.lagou.com
Disallow: /t/temp1/
Disallow: /center/preview.html
Disallow: /center/previewApp.html
Disallow: /*?utm_source=*
Allow: /gongsi/interviewExperiences.html?companyId=*
Disallow: /*?*

我需要的信息在这里:https://www.lagou.com/jobs/list_*
emmmm.....所以,就在刚才,我还是爬了,于是我被封了,这就导致了我现在无法访问。

然后,我们打开拉勾网主页,并搜索‘python’:

image.png

过滤器选‘应届生、本科生’。就可以看到这样的界面:

image.png

我们按F12进入开发者模式:
点击network->XHR——》刷新页面,就可以看到。。。。。。。。。。

对不起,我啥都看不到了,只能看到让我登录的页面。。。。

你们应该能看到有4条记录,然后我们点击第二页,又会出来四条,经过观察我们要的信息在pos*开头的ajax请求的一条,点preview就可看到返回的响应了。

我看到请求的url没变,而且方式是post,就来了兴趣,昨天的是get方法。

headers向下拉,可以看到form信息,和请求参数,
这是我之前保存的:

params:

'xl': '本科',
'px': 'default',
'gx': '全职',
'needAddtionalResult': 'false',
'isSchoolJob': '1'


form:
'first': 'false',
'kd': 'python',
'pn': 2
一个小时后我补上的

观察后发现,第一页的first为true,第二页以后都为false,pn就是页码,所以我们就可以根据此来的到每一页的代码了。

这里注意请求头信息一定要是最新的,不然只能吃灰,就是那个cookies。

于是我就写出了如此完美的请求函数:

def get_one_page(pagenum):
    '''
    pagemun为页数,获取一页的信息
    :param pagenum:
    :return json:
    '''
    params = {
        'xl': '本科',
        'px': 'default',
        'gx': '全职',
        'needAddtionalResult': 'false',
        'isSchoolJob': '1'
    }
    data = {
        'first': 'false' if pagenum != 1 else 'true',
        'kd': 'python',
        'pn': pagenum

    }
    headers = {
        'Host': 'www.lagou.com',
        'Connection': 'keep-alive',
        'Content-Length': '26',
        'Origin': 'https://www.lagou.com',
        'X-Anit-Forge-Code': '0',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Accept': 'application/json, text/javascript, */*; q=0.01',
        'X-Requested-With': 'XMLHttpRequest',
        'X-Anit-Forge-Token': 'None',
        'Referer': 'https://www.lagou.com/jobs/list_python?px=default&gx=%E5%85%A8%E8%81%8C&gj=&xl=%E6%9C%AC%E7%A7%91&isSchoolJob=1&city=%E5%85%A8%E5%9B%BD',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Cookie': 'WEBTJ-ID=20190328100105-169c2078019103-03287e499e7cc-7a1437-1327104-169c207801a0; _ga=GA1.2.1664461408.1553738469; _gid=GA1.2.1954202981.1553738469; user_trace_token=20190328100111-5bf04f0f-50fd-11e9-b40b-525400f775ce; LGUID=20190328100111-5bf053d7-50fd-11e9-b40b-525400f775ce; JSESSIONID=ABAAABAAADEAAFI9211681F55FF42AB9EE85C8EC6C94593; index_location_city=%E5%85%A8%E5%9B%BD; TG-TRACK-CODE=index_search; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22169c40de3e43ad-0357531209afde-7a1437-1327104-169c40de3e524d%22%2C%22%24device_id%22%3A%22169c40de3e43ad-0357531209afde-7a1437-1327104-169c40de3e524d%22%7D; sajssdk_2015_cross_new_user=1; _gat=1; LGSID=20190328201212-b7f050c6-5152-11e9-b9c8-525400f775ce; PRE_UTM=; PRE_HOST=www.baidu.com; PRE_SITE=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DKbms6xfMqIUv7AMjnRPDL-xkTFJ6snHQGL9DF5fQumm%26wd%3D%26eqid%3De29fa16f0000d8bc000000035c9ca96a; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; SEARCH_ID=2798721a148d4f538908ca0bd396d4ac; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1553771063,1553775087,1553775136,1553775450; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1553775450; LGRID=20190328201727-7371eef8-5153-11e9-b831-5254005c3644'
    }
    base_url = "https://www.lagou.com/jobs/positionAjax.json?"
    url = base_url+urlencode(params)
    try:
        response = requests.post(url, headers=headers, data=data)
        print('页面获取成功')
        if response.status_code == 200:
            # print(response.json())
            return response.json()
    except requests.ConnectionError as e:
        print("error", e.args)
        return None

接下来解析数据:

image.png

响应是 json 格式,非常有结构。
我们要找到我们要的信息在哪里,然后提取我们需要的,我为了以后方便数据分析,我保存了不少信息。

非常清晰明了。

def parse_page(json):
    '''
    解析收到的信息
    :param json:
    :return dict:
    '''
    items = json.get('content').get('positionResult').get('result')
    if items:
        for item in items:
            try:
                # job
                positionName = item.get('positionName')
                positionLables = item.get('positionLables')
                salary = item.get('salary')
                jobNature = item.get('jobNature')
                education = item.get('education')
                # company
                city = item.get('city')
                district = item.get('district')
                companyShortName = item.get('companyShortName')
                industryField = item.get('industryField')
                financeStage = item.get('financ eStage')
                companyId = item.get('companyId')
                companyLabelList = item.get('companyLabelList')
                companySize = item.get('companySize')
            except:
                pass
            else:
                yield {
                    # job
                    'positionName': positionName,
                    'positionLables': positionLables,
                    'salary': salary,
                    'jobNature': jobNature,
                    'education': education,
                    # company
                    'city': city,
                    'district': district,
                    'companyShortName': companyShortName,
                    'industryField': industryField,
                    'financeStage': financeStage,
                    'companyId': companyId,
                    'companyLabelList': companyLabelList,
                    'companySize': companySize,
                }

(简书编辑器怎么会出红线啊,我又没写错,真闹心)

得到了信息就是保存了,这跟昨天的是一样的,保存到mongodb数据库:

def save_to_db(item, collection):
    '''
    保存数据导数据库
    :param item:
    :param collection:
    :return:
    '''
    result = collection.insert_one(item)
    print(result)

然后是main函数,链接数据库,衔接函数:

def main(pagenum):
    '''
    主函数,输入页码
    :param pagenum:
    :return:
    '''
    # 链接数据库并选择集合
    print('开始第{0}'.format(pagenum))
    client = MongoClient('mongodb://localhost:27017')
    db = client.lagou
    collection = db.pythonjob
    # 请求ajax数据,返回json
    json = get_one_page(pagenum)
    # 处理返回json数据并保存到的数据库
    for item in parse_page(json):
        print(item['positionName'])
       # 保存到数据库
        save_to_db(item, collection)

然后我们利用进程池,快速爬取,这也可能是我被封的原意之一:

GROUP_START = 1
GROUP_STOP = 29
if __name__ == '__main__':
    # get_one_page(1)
    pool = Pool()
    group = ([x for x in range(GROUP_START, GROUP_STOP+1)])
    print(group)
    pool.map(main, group)
    pool.close()
    pool.join()

还是一样pycharm里面无法运行,我放到cmd里运行:
到115条的时候就被封了:
结果——数据库:

image.png img

一共有429条,我只有115条,不甘心啊!

所以我决定之后学了代理池,回来全给它盘下来。

最后给个源码,注意,一定要改cookies

import requests
import json
from pymongo import MongoClient
from multiprocessing import Pool
from urllib.parse import urlencode
import time

def get_one_page(pagenum):
    '''
    pagemun为页数,获取一页的信息
    :param pagenum:
    :return json:
    '''
    params = {
        'xl': '本科',
        'px': 'default',
        'gx': '全职',
        'needAddtionalResult': 'false',
        'isSchoolJob': '1'
    }
    data = {
        'first': 'false' if pagenum != 1 else 'true',
        'kd': 'python',
        'pn': pagenum

    }
    headers = {
        'Host': 'www.lagou.com',
        'Connection': 'keep-alive',
        'Content-Length': '26',
        'Origin': 'https://www.lagou.com',
        'X-Anit-Forge-Code': '0',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Accept': 'application/json, text/javascript, */*; q=0.01',
        'X-Requested-With': 'XMLHttpRequest',
        'X-Anit-Forge-Token': 'None',
        'Referer': 'https://www.lagou.com/jobs/list_python?px=default&gx=%E5%85%A8%E8%81%8C&gj=&xl=%E6%9C%AC%E7%A7%91&isSchoolJob=1&city=%E5%85%A8%E5%9B%BD',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Cookie': 'WEBTJ-ID=20190328100105-169c2078019103-03287e499e7cc-7a1437-1327104-169c207801a0; _ga=GA1.2.1664461408.1553738469; _gid=GA1.2.1954202981.1553738469; user_trace_token=20190328100111-5bf04f0f-50fd-11e9-b40b-525400f775ce; LGUID=20190328100111-5bf053d7-50fd-11e9-b40b-525400f775ce; JSESSIONID=ABAAABAAADEAAFI9211681F55FF42AB9EE85C8EC6C94593; index_location_city=%E5%85%A8%E5%9B%BD; TG-TRACK-CODE=index_search; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22169c40de3e43ad-0357531209afde-7a1437-1327104-169c40de3e524d%22%2C%22%24device_id%22%3A%22169c40de3e43ad-0357531209afde-7a1437-1327104-169c40de3e524d%22%7D; sajssdk_2015_cross_new_user=1; _gat=1; LGSID=20190328201212-b7f050c6-5152-11e9-b9c8-525400f775ce; PRE_UTM=; PRE_HOST=www.baidu.com; PRE_SITE=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DKbms6xfMqIUv7AMjnRPDL-xkTFJ6snHQGL9DF5fQumm%26wd%3D%26eqid%3De29fa16f0000d8bc000000035c9ca96a; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; SEARCH_ID=2798721a148d4f538908ca0bd396d4ac; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1553771063,1553775087,1553775136,1553775450; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1553775450; LGRID=20190328201727-7371eef8-5153-11e9-b831-5254005c3644'
    }
    base_url = "https://www.lagou.com/jobs/positionAjax.json?"
    url = base_url+urlencode(params)
    try:
        response = requests.post(url, headers=headers, data=data)
        print('页面获取成功')
        if response.status_code == 200:
            # print(response.json())
            return response.json()
    except requests.ConnectionError as e:
        print("error", e.args)
        return None

def parse_page(json):
    '''
    解析收到的信息
    :param json:
    :return dict:
    '''
    items = json.get('content').get('positionResult').get('result')
    if items:
        for item in items:
            try:
                # job
                positionName = item.get('positionName')
                positionLables = item.get('positionLables')
                salary = item.get('salary')
                jobNature = item.get('jobNature')
                education = item.get('education')
                # company
                city = item.get('city')
                district = item.get('district')
                companyShortName = item.get('companyShortName')
                industryField = item.get('industryField')
                financeStage = item.get('financ eStage')
                companyId = item.get('companyId')
                companyLabelList = item.get('companyLabelList')
                companySize = item.get('companySize')
            except:
                pass
            else:
                yield {
                    # job
                    'positionName': positionName,
                    'positionLables': positionLables,
                    'salary': salary,
                    'jobNature': jobNature,
                    'education': education,
                    # company
                    'city': city,
                    'district': district,
                    'companyShortName': companyShortName,
                    'industryField': industryField,
                    'financeStage': financeStage,
                    'companyId': companyId,
                    'companyLabelList': companyLabelList,
                    'companySize': companySize,
                }
def save_to_db(item, collection):
    '''
    保存数据导数据库
    :param item:
    :param collection:
    :return:
    '''
    result = collection.insert_one(item)
    print(result)

def main(pagenum):
    '''
    主函数,输入页码
    :param pagenum:
    :return:
    '''
    # 链接数据库并选择集合
    print('开始第{0}'.format(pagenum))
    client = MongoClient('mongodb://localhost:27017')
    db = client.lagou
    collection = db.pythonjob
    # 请求ajax数据,返回json
    json = get_one_page(pagenum)
    # 处理返回json数据并保存到的数据库
    for item in parse_page(json):
        print(item['positionName'])
       # 保存到数据库
        save_to_db(item, collection)


GROUP_START = 1
GROUP_STOP = 29
if __name__ == '__main__':
    # get_one_page(1)
    pool = Pool()
    group = ([x for x in range(GROUP_START, GROUP_STOP+1)])
    print(group)
    pool.map(main, group)
    pool.close()
    pool.join()

虽然有点小遗憾,但是我分析ajax进行爬取的能力明显提高了。代码更加规范了,我奖励自己再学一章!

之后学好了,会回来补上缺点的——自动获取最新cookies,利用代理池抗封锁。

想吃石锅鱼。

上一篇 下一篇

猜你喜欢

热点阅读