一、聚焦爬虫:
如果想要爬取页面中指定的内容,就要用到聚焦爬虫, 必须建立在通用爬虫的基础上
二、聚焦爬虫的编码流程:
指定URL
发送请求
获取响应数据
数据解析
持久化存储
如何实现数据解析:
正则解析(1个案例)
bs4(BeautifulSoup4)解析(1个案例)
xpath解析(通用性比较强, 3个案例)
数据解析的原理:
进行标签的定位
通过定位到的标签进行取文本和取属性
五、代码实现——正则解析
1、如何用爬虫程序下载一张图片
# 如何用爬虫程序下载一张图片 import requests img_url = "http://www.521609.com/uploads/allimg/140717/1-140GF92503-lp.jpg" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" } response = requests.get(url=img_url, headers=headers) img_data = response.content # 如果下载的是图片,视频或其他文件的数据,统一都为二进制数据,需要用到response.content来获取 with open("./meinv.jpg", 'wb') as f: f.write(img_data)
2、使用urllib模块下载图片(代码非常简洁)
# 使用urllib模块下载图片(代码非常简洁) # 有一个缺陷,就是没有实现UA伪装,如果遇到有UA检测的网站的话,还是得换回requests模块 from urllib import request url = "http://www.521609.com/uploads/allimg/140717/1-140GF92626-lp.jpg" request.urlretrieve(url=url, filename="./qingmeizi.jpg")
3、爬取并下载校花网里面美女校花版块里面的图片
# 案例: 爬取并下载校花网里面美女校花版块里面的图片 import re import os import requests from urllib import request # 1. 指定URL url = "http://www.521609.com/meinvxiaohua/" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" } ex = '<li><a href=.*?<img src="(.*?)" width="160" height="220".*?</a></li>' # 创建一个存放图片的目录 if not os.path.exists('./meinv'): os.mkdir('./meinv') # 2. 发送请求 response = requests.get(url=url, headers=headers) # 3. 获取页面源码数据 page_text = response.text # 4. 数据解析(先要去网页上去分析页面源码里面的标签) url_list = re.findall(ex, page_text, re.S) print(url_list) # 5. 持久化存储 for url in url_list: img_url = "http://www.521609.com" + url img_name = url.split("/")[-1] img_path = "./meinv/" + img_name # ./meinv/10642054117-1.jpg # 使用urllib模块下载图片 request.urlretrieve(img_url, img_path) print("{img_name}图片下载完成".format(img_name=img_name))
注解:re.S:单行匹配
# re.S :单行匹配 import re html = """<li><a href="/meinvxiaohua/1987.html"><img src="/uploads/allimg/090616/10642054117-1.jpg" width="160" height="220" border="0" alt="东莞理工学院城市学院2009"></a><br> <a href="/meinvxiaohua/1987.html" class="title">东莞理工学院城市学院2009</a></li>""" ex = '<li><a href=.*?<img src="(.*?)" width="160" height="220".*?</a></li>' url = re.findall(ex, html, re.S)[0] print(url)
六、代码实现——bs4解析
bs4解析
-
bs4的安装
- pip3 install bs4
- pip3 install lxml
-
bs4的使用:
- from bs4 import BeautifulSoup
-
bs4的解析原理:
- 实例化一个BeautifulSoup对象,将页面源码数据加载到该对象中
- 使用该对象中的相关方法,进行标签内的文本和属性的提取
-
bs4的使用方法:
- 本地加载
- f = open("./meinv.jpg", 'rb')
- soup = BeautifulSoup(f, 'lxml') # 第一个参数是文件句柄
- 网络加载
- page_text = requests.get(url=url).text
- soup = BeautifulSoup(page_text, 'lxml')
- 本地加载
-
soup对象的相关方法: (1)根据标签名查找
- soup.a 获取页面源码中第一个符合要求的标签
(2)获取属性, 返回的永远是一个单数
- soup.a.attrs 获取a所有的属性和属性值,返回一个字典 - soup.a.attrs['href'] 获取href属性 - soup.a['href'] 也可简写为这种形式
(3)获取文本内容
- soup.string: 只可以获取直系标签中的文本内容 - soup.text: 获取标签下的所有文本内容 - soup.get_text(): 获取标签下的所有文本内容 【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容
(4)find():找到第一个符合要求的标签, 返回的永远是一个单数
- soup.find('a') 通过标签名进行数据解析 通过标签属性进行数据解析: - soup.find('a', title="xxx") - soup.find('a', alt="xxx") - soup.find('a', class_="xxx") - soup.find('a', id="xxx")
(5)find_all:找到所有符合要求的标签, 返回的永远是一个列表
- soup.find_all('a') - soup.find_all(['a','b']) 找到所有的a和b标签 - soup.find_all('a', limit=2) 限制前两个
(6)根据选择器选择指定的内容
select选择器, 返回的永远是一个列表: soup.select('#feng') - 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器 - 层级选择器: - 单层级: div > p > a > .lala 只能是下面一级 - 多层级: div .tang a 下面好多级
【注意】select选择器返回永远是列表,需要通过下标提取指定的对象
例如:
from bs4 import BeautifulSoup # 1 读取本地文件的文件句柄 f = open('./test_page.html', 'r', encoding='utf-8') # 2 实例化一个BeautifulSoup对象 soup = BeautifulSoup(f, 'lxml') print(soup)
soup.a
soup.div
soup.a.attrs.get("title")
soup.a.get("href")
七、代码实现——xpath解析
xpath解析
-
特点:
- 通用性非常强
-
安装:
- pip3 install lxml
-
导包:
- from lxml import etree
-
xpath的解析原理
- 实例化一个etree对象, 将页面源码加载到该对象中
- 使用该对象中的xpath方法结合着xpath表达式进行标签的文本和属性的提取
-
实例化对象的方法:
- 本地加载:
- tree = etree.parse("./test_page.html")
- tree.xpath('xpath表达式') # 注意这里最好是使用单引号,不解释, 一会就明白
- 网络加载:
- tree = etree.HTML(page_text)
- tree.xpath('xpath表达式')
- 本地加载:
-
xpath表达式 /: 从标签开始实现层级定位 //: 从任意位置实现标签的定位
属性定位:
语法: tag[@attrName="attrValue"] //div[@class="song"] # 找到class属性值为song的div标签
层级&索引定位:
# 注意索引值是从1开始 //div[@class="tang"]/ul/li[2]/a # 找到class属性值为tang的div的直系子标签ul下的第二个子标签li下的直系子标签a
逻辑运算:
//a[@href="" and @class="du"] # 找到href属性值为空且class属性值为du的a标签
模糊匹配:
//div[contains(@class, "ng")] //div[starts-with(@class, "ta")]
取文本:
//div[@class="song"]/p[1]/text() # 取直系文本内容 //div[@class="tang"]//text() # 取所有文本内容
取属性:
//div[@class="tang"]//li[2]/a/@href
1、爬取58二手房的房源信息(列表页爬取标题和价格, 详情页爬取概况)
# 需求: 爬取58二手房的房源信息(列表页爬取标题和价格, 详情页爬取概况) import requests from lxml import etree from decode_handler import get_crack_text, base64_code url = "https://sz.58.com/ershoufang/?PGTID=0d100000-0000-43ff-252a-3c9a606fba70&ClickID=2" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" } # 获取页面源码数据 page_text = requests.get(url=url, headers=headers).text # 实例化etree对象,进行数据解析 tree = etree.HTML(page_text) li_list = tree.xpath('//ul[@class="house-list-wrap"]/li') all_data_list = list() for li in li_list: title = li.xpath('./div[2]/h2/a/text()')[0] # ./表示的就是我的当前标签(li标签) price_list = li.xpath('./div[3]//text()') price = "".join(price_list) detail_url = li.xpath('./div[2]/h2/a/@href')[0] # 向详情页的URL发送请求,获取详情页的概况数据 detail_page_text = requests.get(url=detail_url, headers=headers).text # 再新实例化一个详情页的etree对象,进行详情页的数据解析 detail_tree = etree.HTML(detail_page_text) desc = "".join(detail_tree.xpath('//div[@id="generalSituation"]/div//text()')) dic = { "title": title, "price": price, "desc": desc } all_data_list.append(dic) print(all_data_list)
# 解析出所有城市名称https://www.aqistudy.cn/historydata/ tree.xpath('//div[@]/li/a/text() | //div[@]/ul/div[2]/li/a/text()')
八、作业
1、爬取贝壳网二手房源
# 需求:爬取贝壳网二手房源 import requests from lxml import etree url = "https://sz.ke.com/api/newhouserecommend?type=1&query=https%3A%2F%2Fsz.ke.com%2Fershoufang%2F" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" } # 获取页面源码数据 page_text = requests.get(url=url,headers=headers).text # 实例化etree对象,进行数据解析 tree = etree.HTML(page_text) li_list = tree.xpath('//ul[@class="sellListContent"]/li') print(li_list) all_data_list=list() for li in li_list: title=li.xpath('./div/div/a/@title')[0] print(title) price=li.xpath('./div/div[2]/div[5]/div//@title')[0] print(price) datail_url=li.xpath('./a/@href')[0] print(datail_url) # 向详情页的url发送请求,获取详情页的概况数据 detail_page_text = requests.get(url=detail_url,headers=headers).text # 再实例化一个详情页的etree对象,进行详情页的数据解析 detail_tree = etree.HTML(detail_page_text) desc = "".join(detail_tree.xpath('//div[@class="content"]ul/li//text()')) dic = { "title": title, "price": price, "desc": desc } all_data_list.append(dic) print(all_data_list)
2、获取彼岸汽车4K图片
# 获取彼岸汽车4K图片 import requests from lxml import etree import os url="http://pic.netbian.com/4kqiche/" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" } if not os.path.exists("./qiche/"): os.mkdir("./qiche/") # 获取页面源码数据 page_text=requests.get(url=url,headers=headers).text # 实例化etree对象,进行数据解析 tree = etree.HTML(page_text) li_list=tree.xpath('//ul[@class="clearfix"]/li') # li标签的对象 print(li_list) for li in li_list: img_url="http://pic.netbian.com" + li.xpath('./a/img/@src')[0] img_name=li.xpath('./a/b/text()')[0].encode('iso-8859-1').decode('gbk') # 处理编码 print(img_name) # 向详情页的URL发送请求,获取详情页的图片数据 img_page=requests.get(url=img_url,headers=headers).content with open('./qiche/'+img_name+'.jpg','wb') as f: f.write(img_page)
3、爬取百度贴吧的评论内容
# 爬取百度贴吧的评论内容 import requests from lxml import etree url = "https://tieba.baidu.com/p/5126900475" comment_url = "https://tieba.baidu.com/p/totalComment?t=1578445848467&tid=6320163250&fid=59099&pn=1&see_lz=0" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" } page_text = requests.get(url=url, headers=headers).text tree = etree.HTML(page_text) div_list = tree.xpath('//div[@class="l_post l_post_bright j_l_post clearfix "]') all_data_list = list() for div in div_list: desc = div.xpath('./div[2]/div[1]/cc/div[2]//text()') all_data_list.append("".join(desc).strip()) print(all_data_list)
4、爬取http://pic.netbian.com/4kqiche/上面的汽车图片和标题
import requests from lxml import etree url = "http://pic.netbian.com/4kqiche/" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" } response = requests.get(url=url, headers=headers) # response.encoding = "utf-8" # 对响应对象进行编码处理 page_text = response.text tree = etree.HTML(page_text) li_list = tree.xpath('//ul[@class="clearfix"]/li') for li in li_list: img_url = "http://pic.netbian.com" + li.xpath('./a/img/@src')[0] title = li.xpath('./a/b/text()')[0] title = title.encode("iso-8859-1").decode("gbk") # 如果针对响应对象处理编码不成功的话,需要针对出现乱码的数据单独进行编码处理 print(img_url, title)