大众点评之线程池实现全站爬取

要想全站爬取,首先需要分商区、菜系,这样得到的数据才全,不然网站默认只显示50页的数据,根本不满足要求。

第一步,获取所有商区和菜系的url从http://www.dianping.com/beijing/food这个网站获取比较简单,就直接在后面贴代码了。
朝外大街: http://www.dianping.com/beijing/ch10/r1466
烧烤:http://www.dianping.com/beijing/ch10/g508
朝外大街烧烤:http://www.dianping.com/beijing/ch10/g508r1466
这样只要得到后面的那个标识,然后拼接就行。

插播一段很简单的代码(每次复制抓包的请求头都要手工加引号使他变成字典格式,为什么不写一段代码自动变成字典格式呢,我见过有些人是使用正则替换,效果都一样):

headers_ = '''
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:zh-CN,zh;q=0.8
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Cache-Control:no-cache
Connection:keep-alive
Host:catfront.dianping.com
Origin:http://www.dianping.com
Pragma:no-cache
Referer:http://www.dianping.com/shop/120054776
User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.61 Safari/537.36'''

headers = {line.split(':')[0].strip():line.split(':',1)[1].strip() for line in headers_.split('\n') if line.strip()}

print(headers)
{'Accept': '*/*', 
 'Accept-Encoding': 'gzip, deflate, sdch',
 'Accept-Language': 'zh-CN,zh;q=0.8', 
 'Cache-Control': 'no-cache',
 'Connection': 'keep-alive',
 'Content-Type': 'text/plain',
 'Host': 'wreport2.meituan.net',
 'Origin': 'http://www.dianping.com',
 'Pragma': 'no-cache',
 'Referer': 'http://www.dianping.com/shop/120054776',
 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.61 Safari/537.36'
 }

得到了这样的字典:

areas = {
  '朝阳区国贸': 'r2578', '朝阳区双井': 'r2579', '朝阳区三里屯': 'r2580',
  '朝阳区对外经贸': 'r2581', '朝阳区酒仙桥': 'r2583', '朝阳区管庄': 'r2584',
  '朝阳区首都机场': 'r2585', '朝阳区十八里店': 'r2586', '朝阳区北苑家园': 'r2870',
  '朝阳区十里堡': 'r2871', '朝阳区东坝': 'r7509', '朝阳区孙河': 'r12012', 
  '朝阳区马泉营': 'r12013', '朝阳区定福庄': 'r12015', '朝阳区四惠': 'r22996', 
  '朝阳区太阳宫': 'r22997', '朝阳区青年路': 'r22998', '朝阳区石佛营': 'r22999',
  '朝阳区甜水园': 'r23000', '朝阳区慈云寺/八里庄': 'r23001', 
  '朝阳区工人体育场': 'r23002', '朝阳区百子湾': 'r23003', 
  '朝阳区传媒大学/二外': 'r23004', '朝阳区双桥': 'r23005', 
  '朝阳区北京欢乐谷': 'r23006', '朝阳区高碑店': 'r23007', 
  '朝阳区北京东站': 'r23008', '朝阳区霄云路': 'r23009', 
  '朝阳区蓝色港湾': 'r23010', '朝阳区燕莎/农业展览馆': 'r23011',
  '朝阳区姚家园': 'r23012', '朝阳区十里河': 'r23013', '朝阳区立水桥': 'r23014',
  '朝阳区小营': 'r23015', '朝阳区北沙滩': 'r23016', '朝阳区大屯': 'r23017', 
  '朝阳区小庄/红庙': 'r23018', '朝阳区常营': 'r23019',
  '朝阳区798/大山子': 'r23020', '朝阳区草房': 'r70269', 
  '朝阳区朝阳公园': 'r81430', '朝阳区世贸天阶': 'r83302',
  '朝阳区东大桥': 'r83304', '朝阳区小红门': 'r85511', '朝阳区广渠门外': 'r89473',
  '朝阳区建外大街': 'r1465', '朝阳区大望路': 'r2078', '朝阳区朝外大街': 'r1466',
  '朝阳区朝阳公园/团结湖': 'r1467', '朝阳区左家庄': 'r1468', 
  '朝阳区亮马桥/三元桥': 'r1469', '朝阳区亚运村': 'r1470', 
  '朝阳区望京': 'r1471', '朝阳区劲松/潘家园': 'r1472', '朝阳区安贞': 'r1473',
  '朝阳区朝阳其它': 'r1474', '朝阳区芍药居': 'r70191', '东城区和平里': 'r2591',
  '东城区东四十条': 'r23021', '东城区雍和宫/地坛': 'r23022', 
  '东城区南锣鼓巷/鼓楼东大街': 'r23023', '东城区北新桥/簋街': 'r23024',
  '东城区光明楼/龙潭湖': 'r23025', '东城区沙滩/美术馆灯市口': 'r23026', 
  '东城区王府井/东单': 'r1475', '东城区建国门/北京站': 'r1476', 
  '东城区东四': 'r1477', '东城区安定门': 'r1478', '东城区朝阳门': 'r1479',
  '东城区东直门': 'r2066', '东城区广渠门内': 'r2590', '东城区左安门': 'r2874',
  '东城区沙子口': 'r2875', '东城区前门': 'r1503', '东城区崇文门': 'r1504', 
  '东城区天坛': 'r1505', '西城区西四': 'r2593', '西城区月坛': 'r2594', 
  '西城区什刹海': 'r2595', '西城区德外大街': 'r2873', 
  '西城区陶然亭': 'r23027', '西城区南菜园/白纸坊': 'r23028',
  '西城区西单': 'r1481', '西城区复兴门': 'r1482', '西城区阜成门': 'r1483',
  '西城区西直门/动物园': 'r1484', '西城区新街口': 'r1485', 
  '西城区地安门': 'r1486', '西城区牛街': 'r2596', '西城区虎坊桥': 'r2597',
  '西城区菜市口': 'r2876', '西城区广内大街': 'r1499',
  '西城区广外大街': 'r1500', '西城区宣武门': 'r1501', '西城区右安门': 'r1994',
  '海淀区双榆树': 'r2587', '海淀区五棵松': 'r2588', '海淀区清河': 'r2589',
  '海淀区远大路': 'r2872', '海淀区香山': 'r7510', '海淀区大钟寺': 'r23029', 
  '海淀区知春路': 'r23030', '海淀区西三旗': 'r23031', 
  '海淀区四季青': 'r23032', '海淀区人民大学': 'r23033',
  '海淀区万柳': 'r23034', '海淀区学院桥': 'r23035', '海淀区军博': 'r23988',
  '海淀区农业大学西区': 'r23989', '海淀区中关村': 'r1488', 
  '海淀区五道口': 'r1489', '海淀区魏公村': 'r1996', '海淀区北太平庄': 'r1490',
  '海淀区苏州桥': 'r1491', '海淀区北下关': 'r1492', 
  '海淀区公主坟/万寿路': 'r1493', '海淀区紫竹桥': 'r1494', 
  '海淀区航天桥': 'r1495', '海淀区上地': 'r1496', '海淀区颐和园': 'r1497',
  '海淀区海淀其它': 'r1498', '海淀区田村': 'r70131', '丰台区北大地': 'r2592',
  '丰台区刘家窑': 'r2877', '丰台区青塔': 'r2878', '丰台区开阳里': 'r2879', 
  '丰台区草桥': 'r2880', '丰台区看丹桥': 'r2881', '丰台区花乡': 'r7040', 
  '丰台区大红门': 'r7041', '丰台区公益西桥': 'r7506', '丰台区云岗': 'r7507',
  '丰台区卢沟桥': 'r7508', '丰台区北京西站/六里桥': 'r23036', 
  '丰台区分钟寺/成寿寺': 'r23037', '丰台区夏家胡同/纪家庙': 'r23038', 
  '丰台区马家堡/角门': 'r23039', '丰台区丽泽桥/丰管路': 'r23040', 
  '丰台区总部基地': 'r25600', '丰台区石榴庄': 'r70275', 
  '丰台区槐房万达广场': 'r70610', '丰台区方庄': 'r1507', 
  '丰台区六里桥/丽泽桥': 'r1508', '丰台区洋桥/木樨园': 'r1995', 
  '丰台区丰台其它': 'r1509', '丰台区宋家庄': 'r70132', 
  '石景山区模式口': 'r2882', '石景山区苹果园': 'r1923', 
  '石景山区古城/八角': 'r1924', '石景山区鲁谷': 'r1926', 
  '石景山区石景山其它': 'r1927', '大兴区亦庄': 'r5959', 
  '大兴区旧宫': 'r5960', '大兴区黄村': 'r5961', '大兴区西红门': 'r7043', 
  '大兴区庞各庄': 'r70633', '大兴区龙湖天街购物中心': 'r85684', 
  '大兴区天宫院': 'r89454', '通州区果园': 'r5956', '通州区梨园': 'r5957',
  '通州区新华大街': 'r5958', '通州区九棵树': 'r7521', 
  '通州区通州北苑': 'r23045', '通州区武夷花园': 'r23990', 
  '通州区马驹桥': 'r25907', '通州区次渠': 'r70618', '通州区北关': 'r85513', 
  '通州区土桥': 'r86548', '通州区宋庄': 'r64881', '通州区西集': 'r64882', 
  '通州区物资学院': 'r64883', '昌平区回龙观': 'r5953', 
  '昌平区天通苑': 'r5954', '昌平区昌平镇': 'r5955', '昌平区小汤山': 'r7042', 
  '昌平区南口镇': 'r23042', '昌平区北七家': 'r23043', '昌平区沙河': 'r23044',
  '昌平区明十三陵': 'r86572', '昌平区居庸关长城': 'r86574',
  '昌平区十三陵水库': 'r86575', '房山区良乡': 'r12011', 
  '房山区仙栖洞': 'r86567', '房山区上方山国家森林公园': 'r86568', 
  '房山区云居滑雪场': 'r86570', '房山区霞云岭国家森林公园': 'r86571',
  '房山区长阳镇': 'r30781', '房山区城关镇': 'r67342', 
  '房山区窦店镇': 'r67346', '房山区阎村镇': 'r67349', '房山区燕山': 'r67350',
  '房山区河北镇': 'r67374', '房山区十渡镇': 'r67376', 
  '房山区青龙湖镇': 'r67384', '顺义区国展': 'r12016', '顺义区顺义': 'r23041',
  '顺义区莲花山滑雪场': 'r86566', '顺义区小汤山/央美博艺艺术馆': 'r86569', 
  '顺义区后沙峪': 'r64877', '顺义区马坡牛栏山': 'r64878', 
  '顺义区南彩': 'r64879', '顺义区石园': 'r64880', '延庆区八达岭镇': 'r65447',
  '延庆区大榆树镇': 'r65448', '延庆区大庄科乡': 'r65449', 
  '延庆区井庄镇': 'r65450', '延庆区旧县镇': 'r65451', 
  '延庆区康庄镇': 'r65452', '延庆区刘斌堡乡': 'r65453', 
  '延庆区千家店镇': 'r65454', '延庆区沈家营镇': 'r65455', 
  '延庆区四海镇': 'r65456', '延庆区香营乡': 'r65457', 
  '延庆区延庆镇': 'r65458', '延庆区永宁镇': 'r65459', 
  '延庆区张山营镇': 'r65460', '延庆区珍珠泉乡': 'r65461', 
  '延庆区延庆区其他': 'r27618', '密云区北庄镇': 'r65429',
  '密云区不老屯镇': 'r65430', '密云区大城子镇': 'r65431', 
  '密云区东邵渠镇': 'r65432', '密云区冯家峪镇': 'r65433', 
  '密云区高岭镇': 'r65434', '密云区古北口镇': 'r65435', 
  '密云区河南寨镇': 'r65436', '密云区巨各庄镇': 'r65437',
  '密云区经济开发区': 'r65438', '密云区密云镇': 'r65439', 
  '密云区穆家峪镇': 'r65440', '密云区十里堡镇': 'r65441', 
  '密云区石城镇': 'r65442', '密云区太师屯镇': 'r65443', 
  '密云区西田各庄镇': 'r65444', '密云区溪翁庄镇': 'r65445', 
  '密云区新城子镇': 'r65446', '密云区密云区其他': 'r27617',
  '怀柔区怀柔区': 'r27615', '门头沟区门头沟区': 'r27614', 
  '平谷区平谷区': 'r27616'
 }
cooks = {
    '私房菜': 'g1338', '水果生鲜': 'g2714', '食品保健': 'g33759', 
     '下午茶': 'g34014', '人气餐厅': 'g34032', '早茶': 'g34055', 
     '福建菜': 'g34059', '饮品店': 'g34236', '北京菜': 'g311', 
     '家常菜': 'g1783', '台湾菜': 'g107', '鲁菜': 'g26483', 
     '川菜': 'g102', '俄罗斯菜': 'g1845', '湘菜': 'g104', 
     '湖北菜': 'g246', '云贵菜': 'g6743', '徽菜': 'g26482', 
     '小龙虾': 'g219', '本帮江浙菜': 'g101', '粉面馆': 'g1817', 
     '粤菜': 'g103', '创意菜': 'g250', '东北菜': 'g106', 
     '新疆菜': 'g3243', '烧烤': 'g508', '西北菜': 'g26481', 
     '素菜': 'g109', '火锅': 'g110', '江河湖海鲜': 'g251', 
     '小吃快餐': 'g112', '日本菜': 'g113', '韩国料理': 'g114', 
     '东南亚菜': 'g115', '西餐': 'g116', '自助餐': 'g111', 
     '面包甜点': 'g117', '其他美食': 'g118'
    }

第二步,得到css属性和显示的文字的对应关系。请参考上一篇文章这样会得到一个字典。每天需要重新获取,当天可以直接使用。

第三步,使用requests循环访问详细商区和菜系,提取出所有商家的url,然后从商家的url中提取出数据。每个商区和菜系下可能有很多页内容,只需要循环抓取就行。

为了爬取速度,我选用线程池来实现(本来还想用scrapy和aiohttp实现,代理不能用就放弃了),用的是python3自带的concurrent.futures下的ThreadPoolExecutor。不懂的可以百度,使用非常方便。

测试的时候发现我在免费网站抓取的代理基本都被大众点评封了(状态码为200,但得到的是垃圾数据),看来他们也在抓代理,然后直接封。所以只是得到了几条数据,不过已经知道爬虫能使用。另外因为只是学习使用,代码并没有异常处理,有实际需求的可以自己加上代理和异常处理。

获取商区和菜系代码:

# -*- coding: utf-8 -*-
"""
date: Mon Nov 26 14:54:50 2018
python: Anaconda 3.6.5
author: kanade
email: [email protected]
"""
import requests
import pyquery


class GetArea(object):
    '''
    获取地区和美食分类
    '''
    def __init__(self):
        doc = self.get_query()
        self.areas = self.get_area(doc)
        self.cooks = self.get_cook(doc)
        
    def get_query(self):
        url = 'http://www.dianping.com/beijing/food'
        headers = {
            'Host':'www.dianping.com',
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.61 Safari/537.36'
        }
        resp = requests.get(url, headers=headers)
        if resp.status_code == 200:
            html = resp.text
            doc = pyquery.PyQuery(html)
            html = doc('script.J_auto-load').html()
            doc = pyquery.PyQuery(html)
            return doc
            
    def get_area(self, doc):
        items = doc('.fpp_business .list').items()
        areas = {}
        for item in items:
            area = item.find('dt a').text()
            lis = item.find('li a').items()
            for li in lis:
                url = li.attr.href
                id_ = url.split('/')[-1] 
                addr = li.text()
                addr = area + addr
                areas.setdefault(addr,id_)
        return areas
    
    def get_cook(self, doc):
        items = doc('.fpp_cooking a').items()
        cooks = {}
        for item in items:
            cook = item.text()
            url = item.attr.href
            _id = url.split('/')[-1]
            cooks.setdefault(cook,_id)
        return cooks


if __name__ == '__main__':
    ga = GetArea()
    print(ga.areas)
    print(ga.cooks)
    

还有一些模块代码比较长,就不贴了。直接放在github上了。
DianPingSpidergithub地址

如果有什么反爬机制比较有意思的,还请留言告诉我,非常感谢。

猜你喜欢

转载自blog.csdn.net/Qwertyuiop2016/article/details/84664203