大众点评之线程池实现全站爬取
要想全站爬取,首先需要分商区、菜系,这样得到的数据才全,不然网站默认只显示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
这样只要得到后面的那个标识,然后拼接就行。
得到了这样的字典:
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。不懂的可以百度,使用非常方便。
测试的时候发现我在免费网站抓取的代理基本都被大众点评封了,看来他们也在抓代理,然后直接封。所以只是得到了几条数据,不过已经知道爬虫能使用。另外因为只是学习使用,代码并没有异常处理,有实际需求的可以自己加上代理和异常处理。
获取商区和菜系代码:
# -*- 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地址
如果有什么反爬机制比较有意思的,还请留言告诉我,非常感谢。
上一篇: python爬虫爬取大众点评店铺简介信息
下一篇: 属性动画