回顾
- 5种反爬机制
- robots.txt:反爬机制,防君子不防小人
- UA检测:UA伪装
- 数据加密
- 图片懒加载
- 代理ip
- requests模块爬取流程:
- 指定url
- 发起请求
- 获取页面数据
- 数据解析
- 持久化存储
- bs4解析:
- 环境安装:bs4、lxml解析器
- 实例化bs对象,将页面源码数据加载到该对象中
- 定位标签
- find(‘a‘,class_=‘xxx‘)
- findall()
- select()
- 大于号,一个层级
- 空格,多个层级
- 将标签中的文本内容获取
- string 返回标签下文本内容
- text 返回标签下所有字标签问本内容
- get_text()
- 获取属性
- a[‘href‘]
xpath使用(【重点】xpath表达式)
- 环境安装:pip install lxml
- 解析原理:
- 获取页面源码数据
- 实例化一个etree的对象,并且将页码源数据加载到该对象中
- 调用该对象的xpath方法进行制定标签的定位
- 【注意】xpath函数必须结合着xpath表达式进行标签定位和内容捕获
- 将html文档或xml文档转换成一个etree对象,然后调用对象中的方法查找指定的节点
- 本地文件:tree = etree.parse(文件名)
tree.xpath(‘xpath表达式‘)
- 网络数据:tree = etree.HTML(网页内容字符串)
tree.xpath(‘xpath表达式‘)
- 本地文件:tree = etree.parse(文件名)
xpath表达式
- / 层级之间的关系
- / 相当于bs4中select中的>
- // 相当于bs4-select中的空格
- 举例:
- /html/head/title 从根目录开始找,html下的 head标签下的 title标签
- //head/title 先找到当前源码中所有的head标签,在找到head标签下的title标签
- //title 找到所有title标签
- 属性定位:
- //div[@class=‘song‘] 定位所有class属性值为song的div标签;[]中必须跟@符号,属性名称前必须有@【语法结构】,返回的是列表
- 层级&索引定位:
- //div[@class=‘tang‘]/ul/li[2]/a 定位所有class属性值为tong的div直系标签 ul标签下的 第二个li标签下的直系字标签 a标签;
- 逻辑运算:
- //a[@href=‘‘ and @class=‘du‘] 定位所有href属性值为空且class属性值为du的所有a标签
- 模糊匹配:
- //div[contain(@class,‘ng‘)] 定位class属性值包含ng的所有div标签
- //div[start-with(@class,‘ta)] 定位class属性值以ta开头的所有div标签
- 取文本
- 表示获取某个标签下的文本内容
- 表示获取某个标签下的文本内容和所有子标签下的文本内容
- //div[@class=‘song‘]/p[1]/text() 获取class属性值为song的所有div标签下的 第一个p字标签 包含的文本
- //div[@class=‘tang]//text() 获取class属性值为tang的所有div标签下的 所有文本,及其字标签下的所有文本,返回的是列表,列表里有多个列表元素
- 取属性
- //div[@class=‘tang‘]//li[2]/a/@href 返回属性对应的属性值
案例:获取58二手房相关房源信息
In [1]:
import requests from lxml import etree url = ‘https://bj.58.com/beijingzhoubian/ershoufang/?PGTID=0d30000c-0000-1175-8e33-a6e941f8aff5&ClickID=1‘ headers = { ‘User-Agent‘:‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER‘ } # 获取源码数据 page_text = requests.get(url=url,headers=headers).text # 实例化etree对象 tree = etree.HTML(page_text) # 调用xpath方法,后去li标签列表 li_list = tree.xpath(‘//ul[@class="house-list-wrap"]/li‘) fp = open(‘58.csv‘,‘w‘,encoding=‘utf-8‘) #遍历列表 for li in li_list: # .开头的意思:进行局部页面解析;./开头表示从li标签开始解析 title = li.xpath(‘./div[2]/h2/a/text()‘)[0] price = li.xpath(‘./div[3]//text()‘) #将价格的三个列表拼接为字符串 price = ‘‘.join(price) fp.write(title+‘:‘+price+‘\n‘) fp.close() print(‘over‘)
over
案例:获取图片
In [27]:
import requests from lxml import etree import os import urllib url = ‘http://pic.netbian.com/4kmeinv/‘ headers = { ‘User-Agent‘:‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER‘ } response = requests.get(url=url,headers=headers) #response.encoding = ‘utf-8‘ if not os.path.exists(‘./imgs‘): os.mkdir(‘./imgs‘) page_text = response.text tree = etree.HTML(page_text) li_list = tree.xpath(‘//dic[@class="slist"]/ul/li‘) # //dic[@class="slist"]//li for li in li_list: img_name = li.xpath(‘./a/b/text()‘)[0] # 处理中文乱码 img_name = img_name.encode(‘iso-8859-1‘).decode(‘gbk‘) img_url = ‘http://pic.netbian.com‘ + li.xpath(‘./a/img/@src‘)[0] img_path = ‘./imgs/‘ + img_name + ‘.jpg‘ urllib.request.urlretrieve(url=img_url,filename=img_path) print(img_path,‘下载成功‘)
案例:煎蛋网中图片数据:http://jandan.net/ooxx
- 第三种反爬机制:数据加密
In [6]:
import requests from lxml import etree import base64 import urllib headers = { ‘User-Agent‘:‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER‘ } url = ‘http://jandan.net/ooxx‘ page_text = requests.get(url=url,headers=headers).text tree = etree.HTML(page_text) img_hash_list = tree.xpath(‘//span[@class="img-hash"]/text()‘) for img_hash in img_hash_list: img_url = ‘http:‘ + base64.b64decode(img_hash).decode() img_name = img_url.split(‘/‘)[-1] urllib.request.urlretrieve(url=img_url,filename=img_name)
爬取站长素材中的简历模板
In [12]:
import requests from lxml import etree import random headers = { ‘Connection‘:‘close‘,# 当请求成功后,马上断开该次请求(及时释放请求池中的资源)) ‘User-Agent‘:‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER‘ } url = ‘http://sc.chinaz.com/jianli/free-%d.html‘ for page in range(1,4): if page == 1: new_url = ‘http://sc.chinaz.com/jianli/free.html‘ else: new_url = format(url%page) response = requests.get(url=new_url,headers=headers) response.encoding = ‘utf-8‘ page_text = response.text tree = etree.HTML(page_text) div_list = tree.xpath(‘//div[@id="container"]/div‘) for div in div_list: detail_url = div.xpath(‘./a/@href‘)[0] name = div.xpath(‘./a/img/@alt‘)[0] detail_page = requests.get(url=detail_url,headers=headers).text tree = etree.HTML(detail_page) download_list = tree.xpath(‘//div[@class="clearfix mt20 downlist"]/ul/li/a/@href‘) download_url = random.choice(download_list) data = requests.get(url=download_url,headers=headers).content file_name = name + ‘.rar‘ with open(file_name,‘wb‘) as fp: fp.write(data) print(file_name,‘下载成功‘)
机械电子工程师简历模板.rar 下载成功 设计师英文简历模板下载.rar 下载成功 化妆师个人简历范文.rar 下载成功
--------------------------------------------------------------------------- OSError Traceback (most recent call last) E:\Anaconda3\lib\socket.py in readinto(self, b) 588 try: --> 589 return self._sock.recv_into(b) 590 except timeout: OSError: [WinError 10051] 向一个无法连接的网络尝试了一个套接字操作。 During handling of the above exception, another exception occurred: KeyboardInterrupt Traceback (most recent call last) <ipython-input-12-e1e8f04f99ec> in <module> 26 download_list = tree.xpath(‘//div[@class="clearfix mt20 downlist"]/ul/li/a/@href‘) 27 download_url = random.choice(download_list) ---> 28 data = requests.get(url=download_url,headers=headers).content 29 file_name = name + ‘.rar‘ 30 with open(file_name,‘wb‘) as fp: E:\Anaconda3\lib\site-packages\requests\api.py in get(url, params, **kwargs) 73 74 kwargs.setdefault(‘allow_redirects‘, True) ---> 75 return request(‘get‘, url, params=params, **kwargs) 76 77 E:\Anaconda3\lib\site-packages\requests\api.py in request(method, url, **kwargs) 58 # cases, and look like a memory leak in others. 59 with sessions.Session() as session: ---> 60 return session.request(method=method, url=url, **kwargs) 61 62 E:\Anaconda3\lib\site-packages\requests\sessions.py in request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json) 531 } 532 send_kwargs.update(settings) --> 533 resp = self.send(prep, **send_kwargs) 534 535 return resp E:\Anaconda3\lib\site-packages\requests\sessions.py in send(self, request, **kwargs) 684 685 if not stream: --> 686 r.content 687 688 return r E:\Anaconda3\lib\site-packages\requests\models.py in content(self) 826 self._content = None 827 else: --> 828 self._content = b‘‘.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b‘‘ 829 830 self._content_consumed = True E:\Anaconda3\lib\site-packages\requests\models.py in generate() 748 if hasattr(self.raw, ‘stream‘): 749 try: --> 750 for chunk in self.raw.stream(chunk_size, decode_content=True): 751 yield chunk 752 except ProtocolError as e: E:\Anaconda3\lib\site-packages\urllib3\response.py in stream(self, amt, decode_content) 492 else: 493 while not is_fp_closed(self._fp): --> 494 data = self.read(amt=amt, decode_content=decode_content) 495 496 if data: E:\Anaconda3\lib\site-packages\urllib3\response.py in read(self, amt, decode_content, cache_content) 440 else: 441 cache_content = False --> 442 data = self._fp.read(amt) 443 if amt != 0 and not data: # Platform-specific: Buggy versions of Python. 444 # Close the connection when no data is returned E:\Anaconda3\lib\http\client.py in read(self, amt) 445 # Amount is given, implement using readinto 446 b = bytearray(amt) --> 447 n = self.readinto(b) 448 return memoryview(b)[:n].tobytes() 449 else: E:\Anaconda3\lib\http\client.py in readinto(self, b) 489 # connection, and the user is reading more bytes than will be provided 490 # (for example, reading in 1k chunks) --> 491 n = self.fp.readinto(b) 492 if not n and b: 493 # Ideally, we would raise IncompleteRead if the content-length E:\Anaconda3\lib\socket.py in readinto(self, b) 587 while True: 588 try: --> 589 return self._sock.recv_into(b) 590 except timeout: 591 self._timeout_occurred = True KeyboardInterrupt:
【重点】
- 问题:往往在进行大量请求发送的时候,经常会报出这样一个错误:HTTPConnectionPool(host:XX)Max retries exceeded with url.
- 原因:
- 1.每次数据传输前客户端要和服务器简历TCP连接,为节省传输消耗,默认为keep-alive,即连接一次,传输多次,然而如果连接迟迟不断开的话,连接池满后则无法产生新的连接对象,导致请求无法发送。
- 2.ip被封
- 3.请求频率太频繁
- 解决:如果下列解决未生效,则可以尝试再次执行程序(因为第一次运行Connection可能不生效)
- 1.设置请求头中的Connection的值为close,表示每次请求成功后断开连接
- 2.更换请求ip(使用非常简单,之间直接在get/post请求中加个参数)
- 3.每次请求之间使用sleep进行等待间隔【不推荐,影响效率】
解析所有城市名称 https://www.aqistudy.cn/historydata/
In [16]:
import requests from lxml import etree import random headers = { ‘Connection‘:‘close‘,# 当请求成功后,马上断开该次请求(及时释放请求池中的资源)) ‘User-Agent‘:‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER‘ } url = ‘https://www.aqistudy.cn/historydata/‘ page_text = requests.get(url=url,headers=headers).text tree = etree.HTML(page_text) li_list = tree.xpath(‘//div[@class="bottom"]/ul/li | //div[@class="bottom"]/ul/div[2]/li‘) for li in li_list: city_name = li.xpath(‘./a/text()‘)[0] print(city_name)
北京 上海 广州 深圳 杭州 天津 成都 南京 西安 武汉 阿坝州 安康 阿克苏地区 阿里地区 阿拉善盟 阿勒泰地区 安庆 安顺 鞍山 克孜勒苏州 安阳 蚌埠 白城 保定 北海 宝鸡 北京 毕节 博州 白山 百色 保山 白沙 包头 保亭 本溪 巴彦淖尔 白银 巴中 滨州 亳州 长春 昌都 常德 成都 承德 赤峰 昌吉州 五家渠 昌江 澄迈 重庆 长沙 常熟 楚雄州 朝阳 沧州 长治 常州 潮州 郴州 池州 崇左 滁州 定安 丹东 东方 东莞 德宏州 大理州 大连 大庆 大同 定西 大兴安岭地区 德阳 东营 黔南州 达州 德州 儋州 鄂尔多斯 恩施州 鄂州 防城港 佛山 抚顺 阜新 阜阳 富阳 抚州 福州 广安 贵港 桂林 果洛州 甘南州 固原 广元 贵阳 甘孜州 赣州 广州 淮安 海北州 鹤壁 淮北 河池 海东地区 邯郸 哈尔滨 合肥 鹤岗 黄冈 黑河 红河州 怀化 呼和浩特 海口 呼伦贝尔 葫芦岛 哈密地区 海门 海南州 淮南 黄南州 衡水 黄山 黄石 和田地区 海西州 河源 衡阳 汉中 杭州 菏泽 贺州 湖州 惠州 吉安 金昌 晋城 景德镇 金华 西双版纳州 九江 吉林 即墨 江门 荆门 佳木斯 济南 济宁 胶南 酒泉 句容 湘西州 金坛 鸡西 嘉兴 江阴 揭阳 济源 嘉峪关 胶州 焦作 锦州 晋中 荆州 库尔勒 开封 黔东南州 克拉玛依 昆明 喀什地区 昆山 临安 六安 来宾 聊城 临沧 娄底 乐东 廊坊 临汾 临高 漯河 丽江 吕梁 陇南 六盘水 拉萨 乐山 丽水 凉山州 陵水 莱芜 莱西 临夏州 溧阳 辽阳 辽源 临沂 龙岩 洛阳 连云港 莱州 兰州 林芝 柳州 泸州 马鞍山 牡丹江 茂名 眉山 绵阳 梅州 宁波 南昌 南充 宁德 内江 南京 怒江州 南宁 南平 那曲地区 南通 南阳 平度 平顶山 普洱 盘锦 蓬莱 平凉 莆田 萍乡 濮阳 攀枝花 青岛 琼海 秦皇岛 曲靖 齐齐哈尔 七台河 黔西南州 清远 庆阳 钦州 衢州 泉州 琼中 荣成 日喀则 乳山 日照 韶关 寿光 上海 绥化 石河子 石家庄 商洛 三明 三门峡 山南 遂宁 四平 商丘 宿迁 上饶 汕头 汕尾 绍兴 三亚 邵阳 沈阳 十堰 松原 双鸭山 深圳 朔州 宿州 随州 苏州 石嘴山 泰安 塔城地区 太仓 铜川 屯昌 通化 天津 铁岭 通辽 铜陵 吐鲁番地区 铜仁地区 唐山 天水 太原 台州 泰州 文昌 文登 潍坊 瓦房店 威海 乌海 芜湖 武汉 吴江 乌兰察布 乌鲁木齐 渭南 万宁 文山州 武威 无锡 温州 吴忠 梧州 五指山 西安 兴安盟 许昌 宣城 襄阳 孝感 迪庆州 锡林郭勒盟 厦门 西宁 咸宁 湘潭 邢台 新乡 咸阳 新余 信阳 忻州 徐州 雅安 延安 延边州 宜宾 盐城 宜昌 宜春 银川 运城 伊春 云浮 阳江 营口 榆林 玉林 伊犁哈萨克州 阳泉 玉树州 烟台 鹰潭 义乌 宜兴 玉溪 益阳 岳阳 扬州 永州 淄博 自贡 珠海 湛江 镇江 诸暨 张家港 张家界 张家口 周口 驻马店 章丘 肇庆 中山 舟山 昭通 中卫 张掖 招远 资阳 遵义 枣庄 漳州 郑州 株洲
反爬机制:图片懒加载
- src属性下有两种图片地址:src/src2,当图片出现在可视化窗口中时出现src,为图片真正地址;当图片不在可视化范围中时图片url为src2;而src2为错误地址,所以当使用爬虫时,出现的是错误的src2地址,所以爬取不到图片。
设置请求的代理IP
- 使用方法:
- 直接在get/post请求中加入proxies={‘类型‘:‘ip‘}
- 代理ip的类型必须和请求url的协议头保持一致
- 提供代理ip的网站:
- www.goubanjia.com
- 快代理
- 西祠代理
- 每种代理ip分两种类型:http/https
- 代理池 [dic1,dic2,dic3...] proxies=
In [24]:
import requests url = ‘https://www.baidu.com/s?wd=ip‘ page_text = requests.get(url=url,headers=headers,proxies={‘https‘:‘36.111.140.6:8080‘}).text with open(‘./ip.html‘,‘w‘,encoding=‘utf-8‘) as fp: fp.write(page_text)
原文地址:https://www.cnblogs.com/bilx/p/11553081.html
时间: 2024-10-09 23:18:00