美文网首页Python
python爬虫07-分析ajax爬取拉勾网职位(一)

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

作者: DKider | 来源:发表于2019-03-28 21:04 被阅读8次

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

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

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

    • 我研究了一会,我发现我的cookies会一直变,如果一段时间间不更新cookise,就会无法访问,弹出访问太频繁的页面。

    • 如果短时间内获取多页,就会被封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,利用代理池抗封锁。

    想吃石锅鱼。

    相关文章

      网友评论

        本文标题:python爬虫07-分析ajax爬取拉勾网职位(一)

        本文链接:https://www.haomeiwen.com/subject/vfacbqtx.html