聚焦爬虫的数据解析
requests
requests:网络请求的模块,模拟浏览器发请求的 - 编码流程: - 指定url - 发起了请求 - 获取响应数据
- 数据解析 - 持久化存储 安装 pip install requests
概念补充
- 反爬 - robots - UA伪装 - 请求载体的身份标识 User_Agent - 在headers种应用一个字典(请求头信息:UA) headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36' } - 动态加载的数据 - 验证我们要爬取的数据,是否为当前页面动态加载出来的? - 是:直接爬取不到 - 不是:可以直接爬取 - 如何验证页面中的局部数据是否为动态加载呢? - 首先在页面中复制一部分的页面内容,然后在通过抓包工具定位到url指定的数据包,在数据包的response中进行刚才复制内容的搜索,搜索到则表示没有动态加载,否则为动态加载!!! - 如何处理动态请求参数: - 封装到一个字典中,字典需要作用到data或者params
简单get请求实例
#爬取搜狗首页的页面源码数据 import requests # 1指定url url = 'https://www.sogou.com/' # 2发起请求:返回值是一个response response = requests.get(url=url) # 3.获取响应数据:text返回的字符串形式的响应数据(页面源码数据) page_text = response.text # 4.持久化存储 with open('sogou.html','w',encoding='utf-8') as fp: fp.write(page_text)
案例:爬取豆瓣电影排名
import requests url = "https://movie.douban.com/j/chart/top_list" headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3861.0 Safari/537.36 Edg/77.0.230.2" } for start in range(0,200,20): data = { 'type': '11', 'interval_id': '100:90', 'action': "", 'start': start, 'limit': '20' } movie_data = requests.post(url=url,data=data,headers=headers).json() for movie in movie_data: score = movie["rating"][0] title = movie["title"] num = movie["rank"] print("评分:"+score,"名字:"+title, num)
数据解析
-- 是为了实现聚焦爬虫
- 数据解析的作用 - 用于获取页面中局部的页面源码数据 - 如何实现数据解析 - 正则 - bs4(python独有) - xpath(最为通用) - pyquery - 数据解析的通用原理是什么? - 标签定位 - 将标签中间存储的文本数据或者其属性值进行捕获
正则
import requests import os import re from urllib import request headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3861.0 Safari/537.36 Edg/77.0.230.2" } # 封装一个通用的url模板 url = 'https://www.qiushibaike.com/pic/page/%d/' dirname = './糗事百科图片' # 如果没有文件夹,新建文件夹 if not os.path.exists(dirname): os.mkdir(dirname) for page in range(1,4): print("正在爬取第{}页的数据".format(page)) new_url = format(url%page) page_text = requests.get(url=new_url,headers=headers).text # 数据解析 ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>' # 匹配出所有的数据 img_src_list = re.findall(ex,page_text,re.S) # re.S可以处理findall中的回车换行 # src是不完整的,需要拼接 for img_src in img_src_list: img_src = "https:"+img_src print(img_src) # 把最后的名字当做文件名存储 img_name = img_src.split("/")[-1] # 放在指定文件夹下 img_path = dirname+'/'+img_name request.urlretrieve(img_src,img_path) print(img_name,"下载成功!!!")
bs4解析
- 环境的安装: - pip install bs4 - pip install lxml - bs4解析原理 - 实例化一个BeautifulSoup的对象,且将即将被解析的页面源码加载到该对象中 - 使用该对象中的属性或者方法进行标签定位和数据提取 - BeautifulSoup对象的实例化方式:lxml就是一款解析器 - BeautifulSoup(fp,'lxml'):将本地存储的html文档加载到该对象中 - BeautifulSoup(page_text,'lxml'):将互联网上获取的html源码加载到该对象中
导入:
from bs4 import BeautifulSoup
soup = BeautifulSoup(fp/page_text,'lxml')
总结
- soup.tagName:返回的就是页面中第一次出现的tagName标签(返回的是一个单数) soup.div # 直接点标签名,返回的是第一个标签内容 - find函数的用法:属性定位 - soup.find('tagName',attrName='value') soup.find('div',class_='song') # 返回的是单数 - soup.find_all('tagName'):定位所有的tagName的标签 - soup.find_all('tagName',attrName='value'):属性定位 soup.find_all('div') # 返回的是列表 - select('选择器'):根据选择器进行标签定位且返回的是复数(列表) - 类选择器,id选择器,标签选择器,层级选择器 - 层级选择器:>表示一个层级,空格表示多个层级 soup.select('.tang > ul > li') == soup.select('.tang li') - 取数据(属性值和标签中存储的文本数据) soup.p.string soup.p.text 取的是标签中所有的文本内容 - text和string的区别? - string获取的是标签中直系的文本内容 - text获取的是标签中所有的文本内容 - 取属性: - tag['attrName'] for a in soup.select('.tang > ul > li > a'): print(a['href'])
bs4实例(水浒传)
from bs4 import BeautifulSoup url = "http://www.shicimingju.com/book/shuihuzhuan.html" page_text = requests.get(url=url,headers=headers).text # 使用bs4解析数据(标题+内容) soup = BeautifulSoup(page_text,"lxml") # 使用层级选择器 a_list = soup.select('.book-mulu > ul > li > a') fp = open("水浒传.txt","w",encoding='utf-8') for a in a_list: title = a.string detail_url = "http://www.shicimingju.com"+a['href'] # 对详情页的url发起请求 detail_text = requests.get(url=detail_url,headers=headers).text detial_soup = BeautifulSoup(detail_text,"lxml") content = detial_soup.find('div',class_="chapter_content").text # 保存到文件 fp.write(title+":"+content+'\n') print(title,"保存成功!!!") fp.close()
xpath解析
- 环境安装:pip install lxml - 解析原理: - 实例化一个etree类型的对象,且将即将被解析的页面源码数据加载到该对象中 - 调用该对象中的xpath方法结合着不同的xpath表达式进行标签定位和数据提取 - 实例化对象: - etree.parse(fileName) - etree.HTML(page_text)
导入
from lxml import etree # from lxml.html.clean import tree
总结
- xpath方法返回值永远是一个列表 - xpath表达式中最左侧的/和//的区别是什么? - /表示我们必须从根标签进行定位 - //表示我们可以从任意位置标签定位 - 属性定位 tree.xpath('//div[@class="song"]') - 索引定位 - 索引值是从1开始 - 在xpath表达式中非最左侧的/和//的区别? - /表示一个层级 - //表示多个层级 - 取文本 - /text():获取的是标签下直系的文本数据 - //text():获取的是标签下所有的文本数据 - 取属性 tree.xpath('//div[@class="tang"]//li[5]/a/@href')
xpath实例(爬boss直聘)
# 通用的url模板 url = "https://www.zhipin.com/c101010100/?query=Java&page=%d" fp = open("boos直聘.txt","w",encoding="utf-8") for page in range(1,4): print("正在爬取第{}页的数据".format(page)) new_url = format(url%page) page_text = requests.get(url=new_url,headers=headers).text # xpath解析 tree = etree.HTML(page_text) # tree中存储的是整个页面对应的页面源码数据 li_list = tree.xpath("//div[@class='job-list']/ul/li") for li in li_list: # li中存储的是局部li标签对应的页面源码数据 job_title = li.xpath('./div/div[1]/h3/a/div[1]/text()')[0] salary = li.xpath('./div/div[1]/h3/a/span/text()')[0] detail_url = "https://www.zhipin.com" + li.xpath('./div/div[1]/h3/a/@href')[0] company = li.xpath('.//div[@class="company-text"]/h3/a/text()')[0] # 详情页发起请求获取岗位描述 detail_page_text = requests.get(url=detail_url,headers=headers).text detail_tree = etree.HTML(detail_page_text) job_desc = detail_tree.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()') job_desc = "".join(job_desc) fp.write(job_title+"-"+salary+"-"+company+"\n"+job_desc+"\n\n\n") fp.close()
补充:中文乱码解决
img_name = img_name.encode("iso-8859-1").decode("gbk")
补充:管道的使用
tree.xpath('//div[@class="bottom"]/ul/li/a/text() | //div[@class="bottom"]/ul/div[2]/li/a/text()')
user = info.xpath( './div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()')[0]