获取页面数据

首先写一个简单的爬虫, 来获取页面数据

class DaZhongDianPing:def __init__(self):self.s = requests.session()self.url = "http://www.dianping.com/shop/k9oYRvTyiMk4HEdQ/review_all"self.headers = {'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36","Cookie": "xxxxxx"}self.html = Nonedef get_html(self):"""获取数据"""response = self.s.get(self.url, headers=self.headers)self.html = response.text

记得添加最关键的user-agent和Cookie, 否则你连最基本的网页都获取不到, 这里我就不把自己的cookie放出来了, 自己登陆后随便打开一条请求后复制即可

使用xpath来获取用户评论信息

    def get_data_by_xpath(self):"""使用xpath获取用户信息"""html_xpath = etree.HTML(self.html)# 获取评论以及用户user_data_list = html_xpath.xpath("//li/div[@class='main-review']//a[@class='name']/text()|""//li/div[@class='main-review']//span[@class='name']/text()")user_data_list = [x.strip() for x in user_data_list]user_comment_list = html_xpath.xpath("//ul/li/div[@class='main-review']/div[@class='review-words']|""//ul/li/div[@class='main-review']/div[@class='review-words Hide']")if len(user_data_list) == len(user_comment_list):print("以使用xpath获取信息...")else:print("获取用户名个数为: ", len(user_data_list))print("获取评论信息个数为: ", len(user_comment_list))raise SyntaxError("用户评论信息不匹配, 请检查xpath...")# pprint(name) ",口味"# 获取用户评论div 转html代码为汉字comments_html_list = [html.unescape(tostring(x).decode('utf-8')) for x in user_comment_list]# pprint(comments_html_list)return comments_html_list, user_data_list

分别在网页中取出用户名和他所发的评论, 因为要直接获取div里的所有内容, 使用xpath的text()就明显不够用了, 直接使用tostring()方法将xpath对象转换为字符串格式, 在使用unescape()将html代码中的字符转换为汉字, 达到能将div中标签和内容一起取出来的效果

解密特殊标签

获取文件, 提取css类

首先获取css文件内容, 类的坐标还有.svg文件都在css文件中

    def get_css_file(self):"""获取css文件名"""# 获取字体文件css_file_name = re.search(r'<link rel="stylesheet" type="text/css" href="(//s3plus.+?\.css)', self.html).group(1)self.css_file_name = css_file_name[css_file_name.rfind('/') + 1:]print("下载文件...", css_file_name)# 下载css文件self.css = self.s.get("http:" + css_file_name).textwith open(os.path.join("dz_decode", self.css_file_name), "w") as f:f.write(self.css)

使用正则取出css文件链接, 提取出三个svg文件

根据css文件中的三个含有.svg文件链接的类, 提取出有用的信息, 黄框为.svg链接, 第一个红框为该字体文件所对应的css类坐标的前缀, 所有以xe为前缀的类所对应的字都存放在这个svg中

另外两个margin就是之前提到的偏移问题, 在最终坐标加去这个值就好了

    def get_csv_file_name(self):# 获取3个csv文件self.file_data_list = re.findall(r'\[class\^="(\w+)"\]\{width: (\d+)px;height: (\d+)px;margin-top: (-?\d+)px;'r'background-image: url\((.+?\.svg)\);background-repeat: no-repeat;display: 'r'inline-block;vertical-align: middle;(margin-left: (-?\d+)px;)?\}',self.css)# pprint(self.file_data_list)# 获取所有的css类coordinate_dict = {}for data in self.file_data_list:css_tuple = re.findall(r'\.(%s\w+)\{background:(-?\d+\.0)px (-?\d+\.0)px;\}' % data[0], self.css)css_list = [{"name": x[0], "x": x[1], "y": x[2]} for x in css_tuple]coordinate_dict[data[0]] = css_list# pprint(coordinate_dict)return coordinate_dictdef get_csv_file(self):# 下载三个文件font_data_list = []for x in self.file_data_list:csv = self.s.get("http:" + x[4]).text# 保存print("正在下载文件...", x[4][x[4].rfind("/") + 1:])with open(os.path.join("dz_decode", x[4][x[4].rfind("/") + 1:]), "w", encoding='utf-8') as f:f.write(csv)# 组合字典font_data_list.append({"class": x[0],"width": x[1],"height": x[2],"top": x[3],"name": x[4][x[4].rfind("/") + 1:],"left": x[6] if x[6] else "0",})return font_data_list

使用正则匹配出三个svg文件的重要信息, 在匹配出所有的css类和x, y的值组成字典返回, 将三个文件进行下载保存, 将类的名称, x, y值提取出来

获取文字坐标

经过多次尝试, svg文件内容分两种境况
格式A:

一种是这种使用<text></text>标签包裹起来, y坐标在text标签中, 使用font-size来计算x值
格式B:

另一种是使用textPath标签包裹, 将y坐标藏在path标签内, 通过id进行映射, 使用font-size来计算x值

    @staticmethoddef get_relationship_by_css(ui_jo_rm, font_data_list):"""根据css文件来获取文字坐标"""csv_coordinate_dict = {}for name_, dict_ in ui_jo_rm.items():# 找到对应的文件及参数data = [x for x in font_data_list if x["class"] == name_][0]# 打开文件with open(os.path.join("dz_decode", data["name"][data["name"].rfind("/") + 1:]), "r",encoding="utf-8") as f:csv_file_list = f.readlines()# 提取字体大小font_size = int(re.search(r'font-size:(\d+)px;', csv_file_list[3]).group(1))  # 14px# 判断文件是什么格式if "defs" not in csv_file_list[4]:# 格式A# 筛选出所需的数据csv_font_list = [x for x in csv_file_list if "/text" in x]# pprint(csv_font_list)# 构筑简短列表 [('41', '太恒烫蓬革益闯禁牲配秃葡席决宣疯雨平榆真浆储纺忠洞挖锤尼爆廊傻悲板造邪圆子喂妹体疆蓝'),csv_font_list = [re.search(r'y="(\d+)">(\w+)</text>', x).group(1, 2) for x in csv_font_list]else:# 格式B# 先获取所有行的y值ys = re.findall(r'<path id="(\d+)" d="M0 (\d+) H600"/>', csv_file_list[4])# 对y进行排序整理# ys.sort(key=lambda x: x[0])new_ys = [x[1] for x in ys]# 获取所有行的字fonts_line = [x for x in csv_file_list if "/textPath" in x]fonts_list = [re.search(r'textLength="\d+">(\w+)</textPath>', x).group(1) for x in fonts_line]# print(new_ys)if len(new_ys) != len(fonts_list):raise SyntaxError(f"正则匹配失败, {len(fonts_line)}, {len(fonts_list)}")# 组合为行列数据csv_font_list = list(zip(new_ys, fonts_list))# pprint(csv_font_list)# pprint(csv_font_list)# 构筑坐标列表  [{'font': '太', 'x': '', 'y': 27},csv_coordinate_list = [{"font": y, "x": i * font_size + abs(int(data['left'])), "y": int(x[0]) + int(data["top"])} for x incsv_font_list for i, y in enumerate(list(x[1]))]# pprint(csv_coordinate_list)# 组合最后的字典csv_coordinate_dict[name_] = csv_coordinate_listreturn csv_coordinate_dict

这里我使用了正则来匹配出需要的信息, 将一列信息和y值匹配出来, 再通过font-size计算x坐标, 减去偏移值后组合成小字典, 将它们放在列表中返回

生成对照表

得到两个关系表后之后的事情就变的很简单了, 只需要判断一个字体数据和css类中的x, y值相近, 这个字就是对应着这个类, 在进行替换之后就能得到解密后的数据了

        for name_, font_list in ui_jo_rm.items():# 提取出字体的xy坐标data = font_coordinate_dict[name_]# 格式化css坐标字典for font in font_list:font['x'] = int(abs(float(font['x'])))font['y'] = int(abs(float(font['y'])))for font_info in data:for font_name in font_list:# 计算偏差的值是否小于设定的值if abs(font_name["x"] - font_info["x"]) <= self.deviation_value and abs(font_name["y"] - font_info["y"]) <= self.deviation_value:# print(font_name["name"], font_info["font"])new_font_coordinate_dict[font_name['name']] = font_info['font']

遍历两个列表, 进行计算差值, 如果差值小于或等于设置的最大偏移量就忽略, 反之将其进行保存, 最终返回这个字典

保存对照信息

进行尝试后发现, 反爬信息是一天进行一次更换, 虽然文件的是动态的但是文件名是固定的, 没有必要每次运行程序都进行一次下载和遍历对照表, 会影响再次访问的速度, 只要判断我是否分析过这个css文件, 如果分析过直接将分析后的对照表返回, 没有在进行对照映射.

        if len(new_font_coordinate_dict) != all_num:print("出现匹配异常数据, 建议加大偏差值...")print("以匹配列表个数", len(new_font_coordinate_dict))print("匹配列表总个数", all_num)else:print("匹配完成, 无异样数据...")# 将数据保存到文件中, 以加快下次解析with open(os.path.join(self.maps_path, self.css_file_name[:-4] + ".wdwj"), 'w', encoding="utf-8") as f:f.write(str(new_font_coordinate_dict))

在匹配无异常时进行数据的储存, 这里是直接将字典进行str之后, 存放在一个文件中, 文件名为原css文件的名字方便下次寻找, 文件的后缀就随便你了

        if read_file:# 判断这次有没有已经映射的文件if os.path.exists(os.path.join(self.maps_path, self.css_file_name[:-4] + ".wdnmd")):print("以使用之前的映射关系进行映射...", os.path.join(self.maps_path, self.css_file_name[:-4] + ".wdwj"))# 打开文件读取with open(os.path.join(self.maps_path, self.css_file_name[:-4] + ".wdnmd"), 'r', encoding="utf-8") as f:file_data = f.read()return eval(file_data)else:print("未找到映射文件, 自动进行映射...")else:print("刷新映射关系")

判断是否开启读取旧文件, 根据有没有这个文件来决定是否进行字体映射, 直接读取文件后使用eval()函数将字符串类型的字典重新格式化为字典

进行文字替换

回到开始, 将最开始获得到的整个评论的div数据进行处理, 提取出文字和类名, 如下图

之后进行对五位编码进行替换, 就能得到包含这篇评论的所有文字, 将他们进行组合就得到了最后完整的评论

    def data_decode(self, comments_html_list):"""解密字体文件"""dzd = DzDecode(self.s, self.html)# 获取解密字典csv_decode_dict = dzd.get_font_map(True)  # True 使用映射文件# print(comments_html_list)# print(comments_tuple_list)# 使用正则匹配出所需信息comments_tuple_list = [re.findall(r'>?>?(\s*?.*?)<\w{7} class="(.{5})"|>?>(.*?)\n', x) for x incomments_html_list]# pprint(comments_tuple_list[0])# 过滤数据 re.sub(r'</svgmtsi', '', x) # pprint(comments_tuple_list[1])comments_tuple_list = eval(re.sub(r"</svgmtsi>|</i>|</div>", '', str(comments_tuple_list)))# 对编码字符进行替换comments_tuple_decrypted_list = []num = 0for comment in comments_tuple_list:char = ""for font in comment:char += font[0]if font[1]:for key, value in csv_decode_dict.items():if key == font[1]:char += valuebreakchar += font[2]comments_tuple_decrypted_list.append(char)if num:print(f"出现异常值, 匹配异常个数为 {num}")comments_tuple_decrypted_list = [x.strip() for x in comments_tuple_decrypted_list]return comments_tuple_decrypted_list

将用户和评论进行对应

最后对应用户和他所发的评论, 爬取评论的需求就完成了

结语

这次代码虽然只打印了用户和评论, 但核心问题已经解决了, 已经得到了三个字体的映射字典, 店铺的手机号, 地址, 评论都可以使用这个字典进行对照, 也没什么需求, 就没去细抠这些东西

历时近3天时间终于把代码写完并完善好了, 刚开是也是处处碰壁, 慢慢的才摸到门路, 天知道我第二天试代码时发现反爬是动态的表情, 世上无难事, 只怕有心人, 我相信只要下定决心就没有解决不了的问题, 看着跑起来的代码还是蛮开心的

本文仅供交流学习,严禁用于商业或着任何违法用途

如有侵权联系删除

完整的视频教程加群:1136192749

爬取大众点评评论-字体加密解析!这个网站很难搞出来!相关推荐

  1. python爬虫实战---爬取大众点评评论

    python爬虫实战-爬取大众点评评论(加密字体) 1.首先打开一个店铺找到评论 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多 ...

  2. python+requests+beautifulsoup爬取大众点评评论信息

    特别声明,此文写于2018年2月,大众点评的页面逻辑,已做了改动,请找最近爬的文章看下,谢谢支持. 先简单聊两句,距离上一篇博客大概过去了4个月,在忙一些别的事情,除了公司有新项目上线,学习新技术之外 ...

  3. python爬取大众点评评论_python爬虫抓取数据 小试Python——爬虫抓取大众点评上的数据 - 电脑常识 - 服务器之家...

    python爬虫抓取数据 小试Python--爬虫抓取大众点评上的数据 发布时间:2017-04-07

  4. python爬取大众点评(破解加密--css加密)

    1.分析 接下来再看一种加密,css加密,以大众点评为例. 访问网址https://www.dianping.com/,搜索关键词比如洗浴,得到如下: 这里的url复制出来之后是这样的:https:/ ...

  5. python爬虫之通过pyquery爬取大众点评评论信息

    写作缘由:朋友求助帮忙爬取一下大众点评天河商圈的商户名, 店铺收藏量, 评论数量, 好评数, 差评数, 口味评分, 环境评分,服务评分, 人均价格, 首页优质评论数. 思路: 1. 我们发现大众点评是 ...

  6. python爬取大众点评字体_python爬取大众点评解决字体反爬

    今天我们弄一下大众点评 学习阶段,我们要抱着学习的目的 重点是思路,做爬虫就不要想着一劳永逸了 方法公开,人家就换了 知识是你自己的,学到了,他换不掉 好了,我们开始吧 网站展示 https://ww ...

  7. python爬取大众点评数据

    python爬取大众点评数据 参考博客: python+requests+beautifulsoup爬取大众点评评论信息 大众点评评论抓取 Chrome如何获得网页的Cookies 如何查看自己访问网 ...

  8. 爬取大众点评数据的血泪史

    公司最近致力于实现餐饮行业的AI发展模式,领导希望采集一些餐饮数据来提供理论支持.所以没多少头发的我 ,被喊过来做数据收集. 想到餐饮数据的收集,第一反应是去爬取美团/大众点评的数据,对比了下美大众点 ...

  9. Python,requests爬虫,使用代理爬取大众点评(含爬取结果。。。在文末)

    由于在工作中,客户需要大众点评的行业数据,因此本人使用Python对大众点评网站进行了爬取,虽然在爬取之前就想好了可能会遇到的坑,但是没想要从坑中爬出来这么难.本次大众点评爬虫代码编写耗时一个月.也算 ...

最新文章

  1. 独家 | 7个新手数据讲述者犯下的致命错误
  2. PL/SQL两种case语句写法
  3. 干货|如何在无回显时渗透
  4. 发生地震等灾难,死难者的存款会怎么处理?
  5. 【转】WPF从我炫系统5---基本控件的用法
  6. 【POJ - 1459】Power Network(网络流最大流,建图)
  7. 1003 我要通过
  8. 运行shell脚本报错:“syntax error near unexpected token fi 的解决方法
  9. STM32 FSMC/FMC原理保姆级讲解(二)
  10. java回车不终止_java 在console行输入一串String后回车,仍无法停止。 下面的程序是计算相似度的,当用户输入关键字后...
  11. 使用Underscore.js的template将Backbone.js的js代码和html代码分离
  12. Atitit.js获取上传文件全路径
  13. params.c:Parameter() - Ignoring badly formed line in configuration file: ignore errors 解决方法
  14. centos7安装桌面
  15. 新手不翻车的可乐鸡翅做法!好吃到吮指!
  16. css图片压缩不变形
  17. 世界上应用最广泛的算法之一的卡尔曼滤波算法原理-从放弃到精通-无人机/机器人应用
  18. Java-实现飞机躲子弹游戏
  19. sklearn中transform和fit_transform的区别(解决中文向量化特征不一致问题)
  20. 测试如何与开发人员进行沟通

热门文章

  1. springboot毕设项目宁夏葡萄酒销售系统g80g5(java+VUE+Mybatis+Maven+Mysql)
  2. 让Android控件随着屏幕旋转自由转移至任何地方(附demo)
  3. PCL:compute3DCentroid ❤️ 计算点云质心
  4. android获取当前连接的蓝牙名称,获取当前连接的蓝牙设备的名称
  5. jms是java平台中面向_Jms学习篇一:JMS介绍
  6. 如何引爆手淘首页流量,手淘首页流量获取玩法
  7. 中国GMP级细胞因子市场现状及未来发展趋势
  8. windows自带w32tm实现做ntp client端,精确同步时间。
  9. c语言二级考试试卷,二级 C语言考试试卷
  10. 抖音怎么申请企业蓝V认证?(含认证教程)