Python爬虫学习笔记

您所在的位置:网站首页 python源码之家网址 Python爬虫学习笔记

Python爬虫学习笔记

2023-03-12 08:45| 来源: 网络整理| 查看: 265

字体反爬+CSS反爬 1. 字体反爬-汽车之家1.1 字体反爬现象1.2 代码思路1.3 完整代码 2. css反爬-自如案例2.1 css反爬现象2.2 代码思路2.3 完整代码

1. 字体反爬-汽车之家 1.1 字体反爬现象

字体反爬通常出现在论坛、小说网站等,因为这些网站的文本内容通常具有较高的商业价值。首先给出相关网站的链接:汽车之家的示例链接 从上图中看,某些文字在开发者工具的element中并没有显示为正常的字体,比如“了”和“一”都显示成了某种特殊字符。上述现象表明网站对论坛中帖子的文本内容做了相关的反爬措施,也就是我们所说的字体反爬。

1.2 代码思路

这里先尝试爬取相关的文本内容:

import requests url = 'https://club.autohome.com.cn/bbs/thread/02b5d5c1fdb18614/93403806-1.html#pvareaid=6830286' req_header = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36' } res_obj = requests.get(url, headers=req_header) print(res_obj.text)

运行结果: 图中显示,我们可以获取到帖子的文本内容,接下来的工作就是筛选所需的内容。笔者这里采用xpath,当然也可以使用其他方法(正则、bs4)看个人喜好,对于该案例,笔者推荐使用xpath。

回顾帖子文本的标签结构: 获取相应div标签下的文本内容:

# 获取网页源代码 origin_text = res_obj.text # 创建xpath对象 html_obj = etree.HTML(origin_text) # 获取对应标签下的所有文本,结果返回一个列表 cont_list = html_obj.xpath('//div[@class="tz-paragraph"]/text()') print(cont_list)

运行结果: 从上图看,目前获取的文本并不完整,因为span标签里面的文本(虽然看起来是乱码)没有获取到,修改xpath语句以获取完整文本:

运行结果: 现在可以获取到完整的代码了,但是乱码显示的内容是’\uee0a’等看似服从某种规律的字符串。实际上,这是网站为了字体反爬而设计的某种特殊字体,让某些文字在网页源代码中显示时变成看不懂的特殊字符串。

如何解决上述特殊字体: Windows系统也存储了许多字体: 查找网站采用的特殊字体:在element里面搜索“myfont”(“myfont”来源于span标签) 这里复制上图中ttf的url,然后粘贴到浏览器进行访问,就可以下载网站的特殊字体文件: 推荐使用FontCreator软件,可以打开该字体文件: 在代码中加载该字体文件:

# pip install fontTools from fontTools.ttLib import TTFont # 加载字体文件,文件已保存到.py同级目录下 ttf_obj = TTFont('ChcCQ1sUz2iANCDMAABj6LtIHts14..ttf') # 打印特殊字体 print(ttf_obj.getGlyphOrder())

运行结果: 上图中,列表里面保存的就是网站使用的特殊字体加载后的字符串,这些字符串与FontCreator软件打开字体文件后显示的汉字一一对应,比如’uniED78’表示汉字“低”。 列表中第一个字符串’.notdef’表示空白,其余字符串中的’uni’表示Unicode,后四位表示具体内容。此处,需要将上述列表进行一定的处理,将’uni’替换为’\u’,与网页源代码中获取到的特殊字符串相对应。另外,特殊字符串对应的汉字,在代码中只能手写出来。

# 处理uni字符串列表 new_uni_list = [] for uni in uni_list[1:]: new_uni = r"'\u" + uni[3:] + "'" new_uni_list.append(new_uni) print(new_uni_list)

运行结果: 此时的结果并不符合我们的预期,还需进行处理,方法如下:

Windows系统自动加上一层斜杠,需采用eval()方法处理后得到想要的结果。 运行结果: 最后一步就是替换文本中的特殊字符串为正常汉字:

# 拼接字符串 contents = ''.join(cont_list) # 准备好正确的汉字列表 word_list = ['', '', '', ''] for i in range(len(new_uni_list)): # 将特殊字符串替换为正常的汉字 contents.replace(new_uni_list[i], word_list[i]) print(contents) 1.3 完整代码 import requests from lxml import etree from fontTools.ttLib import TTFont class TiffSpider(object): def __init__(self, target_url, target_tag, tiff_file, word_list): self.req_header = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, ' 'like Gecko) Chrome/87.0.4280.141 Safari/537.36 ' } self.tiff_file = tiff_file self.target_url = target_url self.target_tag = target_tag self.word_list = word_list # 获取网页源代码 def GetHtmlText(self): res_obj = requests.get(self.target_url, headers=self.req_header) return res_obj.text # 获取原始文本 def GetTagInfo(self, html_text): # 创建xpath对象 html_obj = etree.HTML(html_text) # 获取对应标签下的所有文本,结果返回一个列表 cont_list = html_obj.xpath(self.target_tag) # print(cont_list) contents = ''.join(cont_list) return contents # 替换文本中的特殊字符 def GetResultText(self, contents): # 加载字体文件 ttf_obj = TTFont(self.tiff_file) uni_list = ttf_obj.getGlyphOrder() # 处理uni字符串列表 new_uni_list = [] for uni in uni_list[1:]: new_uni = r"'\u" + uni[3:] + "'" new_uni = eval(new_uni) new_uni_list.append(new_uni) # print(new_uni_list) for i in range(len(new_uni_list)): # 将特殊字符串替换为正常的汉字 contents = contents.replace(new_uni_list[i], self.word_list[i]) return contents # 将结果写入文件 def WriteResult(self, contents): write_contents = "" for index, cont in enumerate(contents): write_contents += cont if index > 0 and index % 80 == 0: write_contents += '\n' with open('result.txt', 'w', encoding='utf-8') as fobj: fobj.write(write_contents) def Run(self): html_text = self.GetHtmlText() origin_contents = self.GetTagInfo(html_text) result_contents = self.GetResultText(origin_contents) self.WriteResult(result_contents) if __name__ == '__main__': url = 'https://club.autohome.com.cn/bbs/thread/02b5d5c1fdb18614/93403806-1.html#pvareaid=6830286' tag = '//div[@class="tz-paragraph"]//text()' file = 'ChcCQ1sUz2iANCDMAABj6LtIHts14..ttf' word = ['低', '右', '大', '十', '得', '七', '一', '着', '是', '远', '少', '上', '矮', '三', '呢', '二', '很', '四', '和', '多', '八', '近', '了', '短', '长', '更', '不', '下', '小', '左', '九', '地', '高', '的', '六', '坏', '五', '好'] tiffspider = TiffSpider(target_url=url, target_tag=tag, tiff_file=file, word_list=word) tiffspider.Run()

运行结果:

2. css反爬-自如案例 2.1 css反爬现象

演示链接:自如的示例链接 自如网站对租房的价格进行了一些css反爬措施: span标签里显示了一张png格式图片的url,在浏览器里面直接访问,可以看到如下的图片,该图片也称为css雪碧图:

百度CSS雪碧: 从字面意思理解,雪碧图通过偏移一定像素来定位所要的图片部分。

以上图为例,偏移-192.6px显示雪碧图中的数字1,偏移-171.2px显示雪碧图中的数字0。

如何寻找偏移量与数字的具体对应关系: 雪碧图中最简单的数字是6,因为它的偏移是0,然后寻找数字9,它的偏移量就是两个数字间的间隔。

笔者总结的雪碧图中数字与偏移量的对应关系:

num_dict = { '6': '-0px', '9': '-21.4px', '4': '-42.8px', '8': '-64.2px', '5': '-85.6px', '2': '-107px', '7': '-128.4px', '3': '-149.8px', '0': '-171.2px', '1': '-192.6px' } 2.2 代码思路

这里先尝试爬取网页的文本内容:

import requests url = 'http://sh.ziroom.com/z/' req_headers = { 'Referer': 'http://sh.ziroom.com/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36' } res_obj = requests.get(url, headers=req_headers) html_text = res_obj.text print(html_text)

运行结果: 尝试获取房屋的名字和价格文本内容(关键标签截图如下):

# 创建xpath对象 html_obj = etree.HTML(html_text) # 获取div[@class="info-box"]标签列表 info_box_list = html_obj.xpath('//div[@class="info-box"]') # 获取房子的名字,以第一个为例 house_name = info_box_list[0].xpath('h5/a/text()') print(house_name) # 获取房子的价格信息,以第一个为例 price_texts = info_box_list[0].xpath('div[@class="price "]/span/@style') print(price_texts)

运行结果: 提取价格信息中数字的偏移量,并转换成最终的价格:

price_num_list = [] for price_text in price_texts: result = re.search(r'.+(background-position: )(\-.+px)', price_text) print(result.group(2)) price = num_dict[result.group(2)] price_num_list.append(price) real_price = ''.join(price_num_list) print(house_name, real_price)

运行结果:

-171.2px -85.6px -192.6px -107px -85.6px ['整租·中环明珠2室1厅-南'] 05125

这里的价格与实际页面上的并不相符,是因为网站的雪碧图在不断变化,导致原有的num_dict无法使用,不过这里笔者主要掌握方法,不要太在意这些。

2.3 完整代码

以下代码中,笔者自己把雪碧图中数字与偏移量的对应关系写在csv文件,如果嫌麻烦也可以写在代码里。

class SpiderZiroom(object): def __init__(self, css_pict_file): self.ziroom_url = 'http://sh.ziroom.com/z/' self.req_headers = { 'Referer': 'http://sh.ziroom.com/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36' } # 传递雪碧图对应的数字文件路径 self.css_pict_file = css_pict_file self.num_dict = {} self.ReadCssPictNum() # 初始化偏移量与数字的对应关系 def ReadCssPictNum(self): with open(self.css_pict_file, 'r') as fobj: conts = csv.DictReader(fobj) for cont in conts: offset = cont['offset'] num = cont['num'] self.num_dict[offset] = num # 获取网页源代码 def GetHtmlText(self): res_obj = requests.get(self.ziroom_url, headers=self.req_headers) html_text = res_obj.text return html_text # 获取对应的文本信息 def GetHouseInfo(self, html_text): html_obj = etree.HTML(html_text) # 获取div[@class="info-box"]标签列表 info_box_list = html_obj.xpath('//div[@class="info-box"]') house_name_list = [] house_priceinfo_list = [] for info in info_box_list: # 获取房子名字 house_name = info.xpath('h5/a/text()')[0] house_name_list.append(house_name) # 获取房子的价格信息 price_texts = info.xpath('div[@class="price "]/span/@style') house_priceinfo_list.append(price_texts) return zip(house_name_list, house_priceinfo_list) # 打包房子的名字和价格到字典 def GetHouseNameAndPrice(self, house_info_zip): result_list = [] for house_name, price_texts in house_info_zip: price_num_list = [] house_info_dict = {} for price_text in price_texts: result = re.search(r'.+(background-position: )(\-.+px)', price_text) # print(result.group(2)) price = self.num_dict[result.group(2)] price_num_list.append(price) real_price = ''.join(price_num_list) # 将结果保存到字典中 house_info_dict['house_name'] = house_name house_info_dict['house_price'] = real_price result_list.append(house_info_dict) return result_list # 将结果保存到本地csv文件 def WriteResult(self, result_list): titles = ['house_name', 'house_price'] with open('ziroom_test.csv', 'w', encoding='utf-8', newline='') as fobj: # 创建writer对象, 第二个参数传递表头,对应数据的key writer_obj = csv.DictWriter(fobj, titles) # 写入表头 writer_obj.writeheader() # 写入内容 writer_obj.writerows(result_list) def run(self): html_text = self.GetHtmlText() house_info_zip = self.GetHouseInfo(html_text) result_list = self.GetHouseNameAndPrice(house_info_zip) self.WriteResult(result_list) if __name__ == '__main__': ziroom = SpiderZiroom('csspictfile.csv') ziroom.run()

运行结果:

house_name,house_price 整租·中环明珠2室1厅-南,10390 合租·南宜花苑4居室-南卧,3860 合租·宏明雅舍3居室-南卧,4890 整租·静安康鑫家园2室1厅-南,13560 合租·金铭福邸4居室-南卧,2030 整租·怡景大厦2室1厅-南,9990 整租·凯旋花苑2室1厅-西,13390 整租·公安住宅小区2室1厅-南,10790 合租·锦华花园3居室-南卧,3090 合租·龙湖北城天街2居室-北卧,2990 合租·紫叶花园东园4居室-南卧,3030 整租·黄山锦庭2室1厅-东南,8990

限于篇幅,以上结果只是其中的一部分。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3