item) # 地址分区
address = re.findall(re.compile(r’(.*?)\s*'), item) # 详细地址
recommend = re.findall(re.compile(
r’<a class=“recommend-click” data-click-name=“shop_tag_dish_click”.*?target=“_blank”>(.*?)'),
item) # 推荐菜
tg_info = re.findall(re.compile(
r’\s*团购:(.*?)\s*'),
item) # 团购信息
coupon = re.findall(re.compile(r’优惠:(.*?)\s*'), item) # 优惠信息
data = []
data.append(img)
data.append(title)
data.append(ifbranch)
data.append(star)
data.append(score)
data.append(numraters)
data.append(avgprice)
data.append(taste)
data.append(enviro)
data.append(serv)
data.append(variety)
data.append(zone)
data.append(address)
data.append(recommend)
data.append(tg_info)
data.append(coupon)
datalist.append(data)
提取结果:
[[‘http://p0.meituan.net/biztone/193986969_1624004529519.jpeg%40340w_255h_1e_1c_1l%7Cwatermark%3D0’], [‘桂满陇-锦绣江南(万象城店)’], [0], ‘45’, ‘4.66’, [‘\uec0e\ue10c1\uf819’], [‘\uec0e\ue498’], [‘\ue82c.\uf819\ue3b9’], [‘\ue82c.\uecd2\ue82c’], [‘\ue82c.\ue3b9\uf819’], [‘浙\ue913’], [‘\uf35e\ue12a\ueac9’], [‘吴\uf136\uf36b1599\uf75f\uea4c象\uf7d04\uf370402A\uf089\ue668’], [‘吮指鸡爪’, ‘石锅沃豆腐’, ‘公主素蟹粉’], [‘桂满陇·西湖船宴!仅售388元!价值454元的招牌必尝四人餐1份,提供免费WiFi。’], []
3. 字体反爬
3.1 字体反爬方式
上图爬出的数据中有较多
\uec0e\ue10c1\uf819
类型的乱码。这是因为大众点评采用Web字体反爬的方式。
”即通过创建自定义字体,改变部分常用字符的Unicode编码。由于服务器端记录了新字体与Unicode编码之间的映射关系,网页端能够识别改变了Unicode编码后的字符并正常显示。然而,当爬虫直接爬取HTML源码并访问子标签值时,由于本地没有对应的字体文件,就无法正常解析Unicode编码,从而显示为方框。“(来源:https://www.cnblogs.com/weosuper/p/13954662.html)
以评价为例,直接爬取的信息会是乱码的形式,因此需要进一步对爬取数据进行解码。
3.2 获取网页字体库
以shopNum字体为例,提取css文件,地址
http://s3plus.meituan.net/v1/mss_0a06a471f9514fc79c981b5466f56b91/svgtextcss/3a1fbc361fa4c6a862e661e34778709f.css
可以看到网页共定义了4种字体:PingFangSC-Regular-shopNum;PingFangSC-Regular-tagName;PingFangSC-Regular-address;PingFangSC-Regular-reviewTag。(进一步分析可发现字体文件名两两一致,即网页端实际上使用了两种字体,这会为之后的解码减轻一定工作量)
搜索找到该URL在HTML源码中的位置
进一步缩小范围,发现svgtextcss可以唯一对应该字段。据此,使用正则表达式提取地址,访问css文件,在解码后的文件中再次使用正则表达式解析得到woff字体文件存储。此段代码如下:
def getWoff(baseURL):
url = baseURL + “1”
html = askURL(url)
print(html)
css_str = re.findall(re.compile(r’href=“(.*?)svgtextcss(.*?).css”>'), html)
css_url = “http:” + css_str[0][0] + “svgtextcss” +css_str[0][1] + “.css”
html_css = str(urllib.request.urlopen(urllib.request.Request(css_url)).read())
#没有登陆、反爬操作,可以不加header直接读取源码
#urllib.request返回的是byte类型
woff_url = re.findall(re.compile(r’//s3plus.meituan.net/v1/mss_\w{32}/font/\w{8}.woff’),html_css)
print(woff_url)
tags = [‘shopNum’,‘tagName’,‘address’,‘reviewTag’]
woff_files = []
for nNum, url in enumerate(woff_url):
res_woff = urllib.request.urlopen(urllib.request.Request(“http:” + url)) #返回类别:HTTPResponce
print(res_woff)
with open(‘./woff/’ + tags[nNum] + ‘.woff’,“wb”) as f:#需要在py文件路径下新建woff文件夹
f.write(res_woff.read()) #读取文件
woff_files.append(‘./woff/’ + tags[nNum] + ‘.woff’)
return(dict(zip(tags, woff_files)))
之后将在新建的woff文件夹中生成4个字体文件:
使用fontcreator打开woff包,可看到字体反爬中加密的字体:
3.3 建立新的映射
建立两个新的映射:shopNum和tagName。其中address与shopNum映射方式一致,tagName与reviewTag映射方式一致。
shopNum的映射方式建立如下所示:
shopNum = TTFont(‘./woff/shopNum.woff’)
woff_unicode = shopNum[‘cmap’].tables[0].ttFont.getGlyphOrder()
woff_glpy_shopNum = []
for i in woff_unicode:
woff_glpy_shopNum.append(i.replace(“uni”, “u”))
woff_str_601 = ‘1234567890店中美家馆小车大市公酒行国品发电金心业商司超生装园场食有新限天面工服海华水房饰城乐汽香部利子老艺花专东肉菜学福饭人百餐茶务通味所山区门药银农龙停尚安广鑫一容动南具源兴鲜记时机烤文康信果阳理锅宝达地儿衣特产西批坊州牛佳化五米修爱北养卖建材三会鸡室红站德王光名丽油院堂烧江社合星货型村自科快便日民营和活童明器烟育宾精屋经居庄石顺林尔县手厅销用好客火雅盛体旅之鞋辣作粉包楼校鱼平彩上吧保永万物教吃设医正造丰健点汤网庆技斯洗料配汇木缘加麻联卫川泰色世方寓风幼羊烫来高厂兰阿贝皮全女拉成云维贸道术运都口博河瑞宏京际路祥青镇厨培力惠连马鸿钢训影甲助窗布富牌头四多妆吉苑沙恒隆春干饼氏里二管诚制售嘉长轩杂副清计黄讯太鸭号街交与叉附近层旁对巷栋环省桥湖段乡厦府铺内侧元购前幢滨处向座下臬凤港开关景泉塘放昌线湾政步宁解白田町溪十八古双胜本单同九迎第台玉锦底后七斜期武岭松角纪朝峰六振珠局岗洲横边济井办汉代临弄团外塔杨铁浦字年岛陵原梅进荣友虹央桂沿事津凯莲丁秀柳集紫旗张谷的是不了很还个也这我就在以可到错没去过感次要比觉看得说常真们但最喜哈么别位能较境非为欢然他挺着价那意种想出员两推做排实分间甜度起满给热完格荐喝等其再几只现朋候样直而买于般豆量选奶打每评少算又因情找些份置适什蛋师气你姐棒试总定啊足级整带虾如态且尝主话强当更板知己无酸让入啦式笑赞片酱差像提队走嫩才刚午接重串回晚微周值费性桌拍跟块调糕’
#这里601个字符的提取方式为:使用OCR软件识别fontcreator中显示的字形,按顺序转换为字符串。此处工作较为繁琐,但目前没有找到比较合适的其他办法
woff_character = [‘.notdef’, ‘x’] + list(woff_str_601)
woff_dict_shopNum = dict(zip(woff_glpy_shopNum, woff_character))
将datalist中的代码转译为字符。
def woff1(num):
num1 = (repr(num).replace(“'”, “”).replace(“[”, “”).replace(“]”, “”).replace(“\\”,“”)).split(“”)
#repr(str):相当于r’,否则转义字符生效,“'”,"\"无法被替换
t = 0
while t < len(num1):
if num1[t][-5:] in woff_dict_shopNum.keys():
#由于并非所有的字符都被加密,会出现”未加密字符+加密字符“的结构。观察发现加密字符长度为5位,这里提取后5位,并将组合结构拆开。
num1.insert(t,num1[t][:-5])
num1[t+1] = woff_dict_shopNum[num1[t+1][-5:]]
elif num1[t][-6:-1] in woff_dict_shopNum.keys():
num1.insert(t,num1[t][-1:])
num1[t+1] = woff_dict_shopNum[num1[t+1][-6:-1]]
elif num1[t][-7:-2] in woff_dict_shopNum.keys():
num1.insert(t,num1[t][-2:])
num1[t+1] = woff_dict_shopNum[num1[t+1][-7:-2]]
#防止后接数字/字符的情况
t = t + 1
return(num1)
def getshopNum(datalist):
for i in datalist:
for j in range (5,10):
num = i[j][0].replace(‘’,“”).replace(“”,“”)
num1 = woff1(num)
i.pop(j)
i.insert(j,num1)
return(datalist)
4. 存储到excel表格中
简单套用代码即可,不再赘述。
def saveData(datalist,savepath):
book = xlwt.Workbook(encoding=“utf-8”, style_compression=0)
sheet = book.add_sheet(“dzdp”, cell_overwrite_ok=True)
col = (“图片”, “店名”,“分店(1:为分店)”, “星级”,“评分”, “评分人数”, “人均价格”,“口味”,“环境”,“服务”, “菜品种类”, “地址分区”,“详细地址”,“推荐菜”,“团购信息”,“优惠信息”)
for i in range(0, 16):
sheet.write(0, i, col[i])
for i in range(0, 150):
print(“第%d条” % i)
data = datalist[i]
for j in range(0, 16):
if j == 13:
sheet.write(i + 1, j, “;”.join(data[j])) #推荐菜用:分隔
else:
sheet.write(i + 1, j, “”.join(data[j]))
book.save(‘dzdp.xls’)
print(“save.”)
def PrintResult(datalist):
result = []
for i in (0 , 16):
res = []
for j in datalist[i]:
res.append(datalist[i][j])
result.append(res)
print(result)
打开excel,可以看到成功提取出150条数据,且不含乱码,爬取成功。
5. 总结
5.1 要点
-
正则表达式测试时,注意每页的信息条数与搜索获得的条数,可以检验正则表达式质量。
-
字体反爬时,容易出错的点在于分隔时将已有的字符与unicode误分到同一个元素中,造成与字库的匹配失败。本文中采用的解决方案是从尾端分隔,并提取相应位数。
做了那么多年开发,自学了很多门编程语言,我很明白学习资源对于学一门新语言的重要性,这些年也收藏了不少的Python干货,对我来说这些东西确实已经用不到了,但对于准备自学Python的人来说,或许它就是一个宝藏,可以给你省去很多的时间和精力。
别在网上瞎学了,我最近也做了一些资源的更新,只要你是我的粉丝,这期福利你都可拿走。
我先来介绍一下这些东西怎么用,文末抱走。
(1)Python所有方向的学习路线(新版)
这是我花了几天的时间去把Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
最近我才对这些路线做了一下新的更新,知识体系更全面了。
(2)Python学习视频
包含了Python入门、爬虫、数据分析和web开发的学习视频,总共100多个,虽然没有那么全面,但是对于入门来说是没问题的,学完这些之后,你可以按照我上面的学习路线去网上找其他的知识资源进行进阶。
(3)100多个练手项目
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了,只是里面的项目比较多,水平也是参差不齐,大家可以挑自己能做的项目去练练。
(4)200多本电子书
这些年我也收藏了很多电子书,大概200多本,有时候带实体书不方便的话,我就会去打开电子书看看,书籍可不一定比视频教程差,尤其是权威的技术书籍。
基本上主流的和经典的都有,这里我就不放图了,版权问题,个人看看是没有问题的。
(5)Python知识点汇总
知识点汇总有点像学习路线,但与学习路线不同的点就在于,知识点汇总更为细致,里面包含了对具体知识点的简单说明,而我们的学习路线则更为抽象和简单,只是为了方便大家只是某个领域你应该学习哪些技术栈。
(6)其他资料
还有其他的一些东西,比如说我自己出的Python入门图文类教程,没有电脑的时候用手机也可以学习知识,学会了理论之后再去敲代码实践验证,还有Python中文版的库资料、MySQL和HTML标签大全等等,这些都是可以送给粉丝们的东西。
这些都不是什么非常值钱的东西,但对于没有资源或者资源不是很好的学习者来说确实很不错,你要是用得到的话都可以直接抱走,关注过我的人都知道,这些都是可以拿到的。
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Python爬虫全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:python)
a77af248b84885a6ec779b2ead064d.png)
这些都不是什么非常值钱的东西,但对于没有资源或者资源不是很好的学习者来说确实很不错,你要是用得到的话都可以直接抱走,关注过我的人都知道,这些都是可以拿到的。
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Python爬虫全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:python)
[外链图片转存中…(img-lbyfskZq-1711001470112)]