一周总结
0927(周一)内容概要
- 解析库之Xpath解析器
- 实战案例之爬取猪八戒数据
0927内容详细
解析库之Xpath解析器
# 效率很高 使用广泛
"""
该选择器可以做到一句话完成多步操作
"""
# 1.导入xpath所在模块
from lxml import etree
# 2.将待匹配的文本传入etree生成一个对象
html = etree.HTML(doc)
# 3.xpath解析器主要功能
# 1 所有节点
a = html.xpath('//*') # 匹配所有的标签
# 2 指定节点(结果为列表)
a = html.xpath('//head') # 匹配所有的head标签
# 3 子节点,子孙节点
a = html.xpath('//div/a') # 匹配div标签内部所有的儿子a标签
a = html.xpath('//body/a') # 没有符合条件的儿子a
a = html.xpath('//body//a') # 匹配div标签内容所有的后代a标签
a = html.xpath('//body//a') # 也可以匹配到
# 4 父节点
a=html.xpath('//body//a[@href="image1.html"]') # 属性查找 获取body内部所有的href=image1.html后代a
a = html.xpath('//body//a[@href="image1.html"]/..') # ..表示查找上一级父标签
a = html.xpath('//title[@id="t1"]/..') # ..表示查找上一级父标签
a = html.xpath('//body//a[1]') # 从1开始取值
'''xpath选择器中中括号内部可以放属性也可以放位置数 从1开始'''
# 也可以这样(了解)
a = html.xpath('//body//a[1]/parent::*')
# 6 文本获取
a = html.xpath('//body//a[@href="image1.html"]/text()')
a = html.xpath('//body//a/text()') # 获取body内部所有后代a内部文本(一次性获取不需要循环)
# 7 属性获取
a = html.xpath('//body//a/@href') # 获取body内部所有后代a标签href属性值(一次性获取不需要循环)
a = html.xpath('//title/@id') # 获取title标签id属性值
# # 注意从1 开始取(不是从0)
a = html.xpath('//body//a[2]/@href')
# 8 属性多值匹配
a 标签有多个class类,直接匹配就不可以了,需要用contains
a=html.xpath('//body//a[@class="li"]') # 写等号就表示等于 不是包含
a = html.xpath('//body//a[contains(@class,"li")]/text()') # 包含需要使用关键字contains
# 9 多属性匹配
# '''查找body标签内部所有class含有li或者name=items的a标签'''
a = html.xpath('//body//a[contains(@class,"li") or @name="items"]')
'''查找body标签内部所有class含有li并且name=items的a标签的内部文本'''
a = html.xpath('//body//a[contains(@class,"li") and @name="items"]/text()')
# 10 按序选择
# 取最后一个
a = html.xpath('//a[last()]/@href')
# 位置小于3的
a = html.xpath('//a[position()<3]/@href') # position()关键字 用于定位
# 倒数第三个
a = html.xpath('//a[last()-2]/@href')
了解
# 11 节点轴选择
# ancestor:祖先节点
# 使用了* 获取所有祖先节点
a = html.xpath('//a/ancestor::*')
# # 获取祖先节点中的div
a = html.xpath('//a/ancestor::div')
# attribute:属性值
a = html.xpath('//a[1]/attribute::*') # 查找a标签内部所有的属性值
# child:直接子节点
a = html.xpath('//a[1]/child::*')
# descendant:所有子孙节点
a = html.xpath('//a[6]/descendant::*')
# following:当前节点之后所有节点
a = html.xpath('//a[1]/following::*')
a = html.xpath('//a[1]/following::*[1]/@href')
# following-sibling:当前节点之后同级节点
a = html.xpath('//a[1]/following-sibling::*')
a = html.xpath('//a[1]/following-sibling::a')
a = html.xpath('//a[1]/following-sibling::*[2]/text()')
a = html.xpath('//a[1]/following-sibling::*[2]/@href')
print(a)
xpath爬取猪八戒数据
import requests
from bs4 import BeautifulSoup
from lxml import etree
res = requests.get('https://shanghai.zbj.com/search/f/',
params={'kw': 'python'}
)
x_html = etree.HTML(res.text)
# 分析标签特征并书写xpath语法筛选
# 1.先查找所有的外部div标签
div_list = x_html.xpath('/html/body/div[6]/div/div/div[2]/div[5]/div[1]/div') # 利用浏览器自动生成
# div_list = x_html.xpath('//div[@class="new-service-wrap"]/div') # 自己写
# 2.循环获取每一个div标签
for div in div_list:
price = div.xpath('.//span[@class="price"]/text()')
company_name = div.xpath('./div/div/a[1]/div[1]/p/text()')
order_num = div.xpath('./div/div/a[2]/div[2]/div[1]/span[2]/text()')
info = div.xpath('./div/div/a[2]/div[2]/div[2]/p/text()')
print(info)
"""
研究./div/div/a[2]/div[2]/div[1]/span[2]表达式作用
"""
0928(周二)内容概要
- 爬取城市名称
- 爬取猪八戒数据并写入表格
- 爬取贴吧图片数据
- 自动化测试工具selenuim模块
0928内容详细
爬取城市名称
import requests
from lxml import etree
# 1.发送请求获取页面数据
res = requests.get("https://www.aqistudy.cn/historydata/",
headers={
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"
}
)
# 2.查看是否有防爬和编码问题
# print(res.text) # 校验User-Agent
# 3.生成一个xpath对象
tree = etree.HTML(res.text)
# 4.研究标签规律 书写xpath提取对应数据
# 先获取热门城市
'''使用xpath选择器 开始的时候可以一层一层往下查找 无需跳跃'''
hot_city_names = tree.xpath('//div[@class="hot"]/div[2]/ul/li/a/text()')
'''使用xpath选择器解析出来的结果肯定是一个列表'''
# 再获取其他城市
other_city_names = tree.xpath('//div[@class="all"]/div[2]/ul/div[2]/li/a/text()')
# 一次性查找所有的城市名称
all_city_names = tree.xpath(
'//div[@class="hot"]/div[2]/ul/li/a/text() | //div[@class="all"]/div[2]/ul/div[2]/li/a/text()')
print(all_city_names)
"""
使用xpath的规律就在于先明确你需要查找的标签
之后往上多看几层具有一定特征的父标签 之后依次逐层查找即可
"""
# 课外扩展:研究详情页历史数据是否可以爬取
爬取猪八戒数据
# 需求:公司名称 地址 价格 成交量 描述信息
import requests
from lxml import etree
from openpyxl import Workbook
wb = Workbook()
wb1 = wb.create_sheet('订单数据', 0)
wb1.append(['公司名称', '公司地址', '订单价格', '历史成交', '订单描述'])
# 1.发送请求获取页面数据
res = requests.get('https://shanghai.zbj.com/search/f/',
params={'kw': 'app'}
)
# 2.生成xpath对象
tree = etree.HTML(res.text)
# 3.研究标签规律 书写xpath
# 直接查找
'''直接查找很多时候是无法使用的 因为会出现数据混乱的现象'''
# company_name = tree.xpath('//div[@class="new-service-wrap"]/div/div/div/a/div[1]/p/text()')
# print(company_name)
# 先查找所有含有数据的div 之后依次循环
div_list = tree.xpath('//div[@class="new-service-wrap"]/div')
for div in div_list:
# 公司名称
company_name = div.xpath('./div/div/a/div[1]/p/text()')
# 如果获取不到公司名称 在该网站上是广告位
if not company_name:
continue
# print(company_name[-1].strip('\n'))
# 公司地址
address_info = div.xpath('./div/div/a/div[1]/div/span/text()')
# print(address_info[0])
# 订单价格
order_price = div.xpath('./div/div/a[2]/div[2]/div[1]/span[1]/text()')
# print(order_price[0])
# 历史成交
order_num = div.xpath('./div/div/a[2]/div[2]/div[1]/span[2]/text()')
# print(order_num[0])
# 订单描述
order_desc = div.xpath('./div/div/a[2]/div[2]/div[2]/p/text()')
# print('app'.join(order_desc))
wb1.append([company_name[-1].strip('\n'), address_info[0], order_price[0], order_num[0], 'app'.join(order_desc)])
wb.save(r'订单数据.xlsx')
# 扩展:针对多页数据此处使用后续模块会更加方便
爬取贴吧图片数据
# 需求:贴吧名称是用户自己指定 不是固定的一个
在编写程序的时候可以先以固定的为例之后换成用户输入即可
所有的图片都需要自动保存到以贴吧名称命名的文件夹内
贴吧图片支持多页爬取
# 前期地址研究
https://tieba.baidu.com/f?ie=utf-8&kw=%E4%B8%83%E9%BE%99%E7%8F%A0&fr=search
https://tieba.baidu.com/f?ie=utf-8&kw=%E7%BE%8E%E5%A5%B3&fr=search
https://tieba.baidu.com/f?ie=utf-8&kw=%E8%BE%A3%E5%A6%B9&fr=search
'''核心就在于kw参数'''
https://tieba.baidu.com/f?kw=%E4%B8%83%E9%BE%99%E7%8F%A0&ie=utf-8&pn=50
https://tieba.baidu.com/f?kw=%E4%B8%83%E9%BE%99%E7%8F%A0&ie=utf-8&pn=100
https://tieba.baidu.com/f?kw=%E4%B8%83%E9%BE%99%E7%8F%A0&ie=utf-8&pn=150
'''核心就在于pn参数'''
# 整体逻辑 先获取每个帖子的链接 之后请求再筛选图片
import requests
from lxml import etree
import os
import time
# 获取用户想要爬取的贴吧名称
tieba_name = input('请输入你想要爬取的贴吧名称>>>:').strip()
# 判断当前贴吧名称是否存在对应的文件夹
if not os.path.exists(tieba_name):
os.mkdir(tieba_name)
# 1.发送请求
# TODO:多页数据 只需要再加一个pn参数即可
res = requests.get('https://tieba.baidu.com/f',
params={'kw': tieba_name}
)
# 2.生成一个xpath对象
tree = etree.HTML(res.text)
# 3.查找所有帖子的链接地址
a_link_list = tree.xpath('//a[@class="j_th_tit "]/@href')
base_url = 'https://tieba.baidu.com'
# 4.循环获取每一个帖子链接 拼接成完整的地址 再发送请求
for link in a_link_list:
full_link = base_url + link
# 5.发送详情页请求获取页面数据
res1 = requests.get(full_link)
tree1 = etree.HTML(res1.text)
# 6.筛选图片链接地址
img_src_list = tree1.xpath('//img[@class="BDE_Image"]/@src')
# 7.循环请求每个图片地址 并保存图片
for img_src in img_src_list:
res2 = requests.get(img_src)
file_path = os.path.join(tieba_name,img_src[-15:])
with open(file_path,'wb') as f:
f.write(res2.content)
time.sleep(1)
selenium模块
原本仅仅是测试领域里面的一款测试工具
但是由于其可以操作浏览器所以逐步也被应用到了爬虫领域
可以避免很多防爬措施但是由于需要操控浏览器所以效率上偏慢
# 1.模块下载
pip3 install selenium
# 2.驱动下载(必备条件)
该模块是用来操作浏览器的 需要相应的驱动软件
"""
注意:有时候下载了驱动可能也无法操作浏览器
原因:可能是因为驱动版本不对
措施:重新一个版本的驱动
注意:不同的浏览器需要下载不同的驱动文件
"""
# 3.驱动文件的存放位置
1.当前编程环境下(不推荐)
2.任意位置只要能记住绝对路径(不推荐)
3.存放到python解释器scripts文件夹内即可(推荐)
一劳永逸
# 4.验证
from selenium import webdriver
import time
# 指定操作的浏览器驱动
bro = webdriver.Chrome()
# 控制浏览器访问网站数据
bro.get("http://www.baidu.com")
# 关闭浏览器窗口
time.sleep(3)
bro.close()
基本操作
""""
find_element与find_elements的区别就在于前者只会找到符合条件的第一个 后者是所有
两者的关系相当于bs4模块里面的find与find_all
"""
# 1、find_element_by_id 根据id找
# div_tag = bro.find_element_by_id('s-top-left')
# 2、find_element_by_link_text 根据链接名字找到控件(a标签的文字)
# a_tag = bro.find_element_by_link_text("新闻")
# 3、find_element_by_partial_link_text 根据链接名字找到控件(a标签的文字)模糊查询
# 4、find_element_by_tag_name 根据标签名
# 5、find_element_by_class_name 根据类名
# 6、find_element_by_name 根据属性名
# 7、find_element_by_css_selector 根据css选择器
# 8、find_element_by_xpath 根据xpath选择
小案例
from selenium import webdriver
from selenium.webdriver.common.keys import Keys # 键盘按键操作
import time
bro = webdriver.Chrome()
# 打开淘宝
bro.get('https://www.taobao.com/')
# 查找搜索框标签
input_tag = bro.find_element_by_id('q')
# 输入文本内容
input_tag.send_keys('iphone手机')
time.sleep(1)
# 点击搜索
input_tag.send_keys(Keys.ENTER)
time.sleep(5)
# 关闭浏览器
bro.close()
0929(周三)内容概要
-
selenuim其他操作
-
图片验证码与滑动验证码的破解思路
-
大型复杂爬虫案例讲解
1.b站视频 2.知乎登录 3.小说爬取 """掌握思路 能够说出个大概 主要用于面试环节"""
0929内容详细
selenuim其他操作
# 获取属性
tag.get_attribute('src')
# 获取文本内容
tag.text
#获取标签ID,位置,名称,大小(了解)
print(tag.id)
print(tag.location)
print(tag.tag_name)
print(tag.size)
# 模拟浏览器前进后退
browser.back()
browser.forward()
# cookies管理
browser.get_cookies() # 获取cookie
browser.add_cookie({'k1':'xxx','k2':'yyy'}) # 设置cookie
# 运行js
from selenium import webdriver
import time
bro=webdriver.Chrome()
bro.get("http://www.baidu.com")
bro.execute_script('window.scrollTo(0,200)') # 鼠标滚轮移动
time.sleep(5)
# 选项卡管理
import time
from selenium import webdriver
browser=webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.execute_script('window.open()')
print(browser.window_handles) # 获取所有的选项卡
browser.switch_to_window(browser.window_handles[1])
browser.get('https://www.taobao.com')
time.sleep(3)
browser.switch_to_window(browser.window_handles[0])
browser.get('https://www.sina.com.cn')
browser.close()
# 动作链(滑动验证码没有代码破解的必要 不如手动滑获取cookie即可)
# 动作链(页面上嵌套页面>>>iframe)
from selenium import webdriver
from selenium.webdriver import ActionChains
import time
driver = webdriver.Chrome()
driver.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
driver.switch_to.frame('iframeResult') # 必须要指定iframe标签
sourse = driver.find_element_by_id('draggable')
target = driver.find_element_by_id('droppable')
# 方式一:基于同一个动作链串行执行(速度太快不合理)
# actions = ActionChains(driver) # 拿到动作链对象
# actions.drag_and_drop(sourse, target) # 把动作放到动作链中,准备串行执行
# actions.perform()
# 方式二:不同的动作链,每次移动的位移都不同
actions = ActionChains(driver)
actions.click_and_hold(sourse)
distance = target.location['x'] - sourse.location['x']
track = 0
while track < distance:
actions.move_by_offset(xoffset=2, yoffset=0).perform()
track += 5
time.sleep(0.5)
actions.release()
driver.close()
iframe界面
"""
有时候一个页面上还会叠加其他完整的html页面
该页面一般都是iframe标签 内部含有完整的html文档结构
在查找该标签内部的标签时需要指定一个参数
driver.switch_to.frame('iframeResult')
"""
滑动验证码
针对滑动验证码也是可以通过selenuim自动完成的
# 滑动验证码很多时候不推荐使用程序破解 太过繁琐
很多时候还不如自己亲自手动滑动来的方便
"""
滑动验证码在拖动的时候速度不能太快 内部有监测机制
速度过快一步到位会被认为是爬虫程序
"""
无界面操作
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
bro = webdriver.Chrome(chrome_options=chrome_options)
bro.get('https://www.baidu.com')
# 如何获取页面html代码
driver.page_source '''可以去药品许可证界面尝试'''
针对selenuim防爬
# 很多程序是可以分辨出来当前浏览器是否被selenuim操作 我们可以在代码中添加如下配置即可避免被识别
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_experimental_option('excludeSwitchers',['enable-automation'])
bro = webdriver.Chrome(options=option)
cookie登录
import requests
from selenium import webdriver
import time
import json
# 使用selenium打开网址,然后让用户完成手工登录,再获取cookie
# url = 'https://account.cnblogs.com/signin?returnUrl=https%3A%2F%2Fwww.cnblogs.com%2F'
# driver = webdriver.Chrome()
# driver.get(url=url)
# time.sleep(30) # 预留时间让用户输入用户名和密码
# driver.refresh() # 刷新页面
# c = driver.get_cookies() # 获取登录成功之后服务端发返回的cookie数据
# print(c)
# with open('xxx.txt', 'w') as f:
# json.dump(c, f)
cookies = {}
with open('xxx.txt', 'r') as f:
di = json.load(f)
# 获取cookie中的name和value,转化成requests可以使用的形式
for cookie in di:
cookies[cookie['name']] = cookie['value']
# # 使用该cookie完成请求
# response = requests.get(url='https://i-beta.cnblogs.com/api/user', cookies=cookies)
# response.encoding = response.apparent_encoding
# print(response.text)
"""
seleuinm拿cookie
requests拿cookie去模拟爬取数据
"""
图片验证码
思路1: 完全使用代码破解
图像识别技术
软件:Tesseract-ocr
模块:pytesseract
思路2: 打码平台
花钱买第三方服务
先使用代码识别如果不想其实还有一帮员工肉眼识别
思路3: 自己人工智能识别
b站视频案例
# 代码无需掌握 只需要思路会说即可
https://www.cnblogs.com/xiaoyuanqujing/articles/12016934.html
https://www.cnblogs.com/xiaoyuanqujing/articles/12014416.html
"""
b站有很视频是一分为二的
分为视频(只有画面没有声音)和音频(视频配套的声音)
"""
红薯网小说案例
# 密码:xiaoyuanqujing@666
1.小说详情页面鼠标左右键全部禁用
但是支持按F12调出控制台
2.小说文字不是直接加载
查找相关二次请求
3.在请求中发现可疑数据
https://www.hongshu.com/bookajax.do
content:加密数据
other:加密数据
bid: 3052
jid: 3317
cid: 98805
4.文字内容的解密过程发送在浏览器本地
涉及到数据解密肯定需要写js代码 并且一般都会出现关键字decrypt
通过浏览器查找相应的js代码
文字主要内容的界面
utf8to16(hs_decrypt(base64decode(data.content), key))
解密之后仍然存在数据缺失的情况
utf8to16(hs_decrypt(base64decode(data.other), key))
解密之后是一段js代码
# 怀疑缺失的数据与解析出来js代码有很大的关系
5.自己新建一个html文件
将content内部拷贝只body内
将js代码引入到该html文件夹
https://www.cnblogs.com/xiaoyuanqujing/protected/articles/11868250.html
0930(周四)内容概要
-
作业讲解
百度自动登录 爬取京东商品数据
-
知乎登录防爬措施
知乎的策略已经修改了 但是不影响我们学习防爬策略
-
爬虫框架之Scrapy
框架:类似于房屋的钢筋结构(使用框架就相当于提前搭建好了结构只需要往不同的结构中添加不同的内容即可产生不同的效果)
0930内容详细
百度自动登录
from selenium import webdriver
import time
# 1.使用谷歌浏览器访问百度首页
bro = webdriver.Chrome()
bro.get('https://www.baidu.com/')
# 2.查找页面上的登录按钮
login_tag = bro.find_element_by_id('s-top-loginbtn')
# 3.点击登录按钮
time.sleep(0.5)
login_tag.click()
'''延迟等待:页面数据有时候需要时间加载 但是代码不会自动等'''
time.sleep(3)
# 4.查找点击短信登录按钮
message_tag = bro.find_element_by_id('TANGRAM__PSP_11__changeSmsCodeItem')
message_tag.click()
time.sleep(0.5)
# 5.查找手机号输入框并填写内容
phone_tag = bro.find_element_by_id('TANGRAM__PSP_11__smsPhone')
phone_tag.send_keys(18818188888)
time.sleep(0.5)
# 6.查找发送验证码按钮并点击
btn_tag = bro.find_element_by_id('TANGRAM__PSP_11__smsTimer')
btn_tag.click()
time.sleep(0.5)
# 7.查找并点击登录按钮
submit_tag = bro.find_element_by_id('TANGRAM__PSP_11__smsSubmit')
submit_tag.click()
time.sleep(0.5)
bro.close()
延时等待
有时候我们在访问网站数据的时候 加载需要一定的时间 没有加载完全的情况下代码极其容易报错 此时需要我们的等待页面数据加载完毕 如何精确等待?
bro.implicitly_wait(10)
京东商品数据
1.商品数据的展示页存在动态加载的情况 需要往下滚动
window.document.body.scrollHeight # 当前页面屏幕高度
window.scrollTo # 滚动到指定位置
2.针对图片数据存在懒加载现象
3.多页数据的爬取
由于我们使用的是selenuim所以此处只需要查找下一页按钮点击即可
将单页爬取数据的代码封装成一个函数 之后就可以循环爬取多页
4.数据的持久化
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
bro = webdriver.Chrome()
# 1.访问京东首页
bro.get('https://www.jd.com/')
bro.implicitly_wait(10) # 延时等待
# 2.查找搜索款并输入商品名称
search_input = bro.find_element_by_id('key')
search_input.send_keys('显卡')
# 3.按下enter键进入商品展示页
search_input.send_keys(Keys.ENTER)
# 由于数据存在滚动加载的现象
for i in range(0, 8000, 1000): # 千万不能直接到底 一定要有一个波段
bro.execute_script('window.scrollTo(0,%s)' % i)
time.sleep(0.5)
good_list = bro.find_elements_by_css_selector('li.gl-item')
# 循环获取每一个li标签 筛选所需数据
for li in good_list:
# 图标标签的src属性
img_tag = li.find_element_by_css_selector('div.p-img a img')
img_src = img_tag.get_attribute('src')
'''img标签的src属性存在懒加载现象 src没有就在data-lazy-img属性下'''
if not img_src:
img_src = 'https:' + img_tag.get_attribute('data-lazy-img')
# 商品价格
price_tag = li.find_element_by_css_selector('div.p-price strong')
order_price = price_tag.text
# 商品描述
desc_tag = li.find_element_by_css_selector('div.p-name a em')
order_desc = desc_tag.text
# 商品链接
link_tag = li.find_element_by_css_selector('div.p-name a')
order_link = link_tag.get_attribute('href')
# 商品销量
commit_tag = li.find_element_by_css_selector('div.p-commit strong a')
order_commit = commit_tag.text
# 店铺名称
shop_tag = li.find_element_by_css_selector('div.p-shop span a')
shop_name = shop_tag.text
# 店铺链接
shop_link = shop_tag.get_attribute('href')
# 通过打印展示数据 也可以数据持久化到表格文件
print("""
商品描述:%s
商品价格:%s
商品图片:%s
商品链接:%s
店铺名称:%s
店铺链接:%s
""" % (order_desc, order_price, img_src, order_link, shop_name, shop_link))
bro.close()
知乎登录案例
1.电脑端知乎不登陆是无法直接访问首页的
2.network监控发送登录请求体数据为加密
加密的代码关键字:encrypt
解密的代码关键字:decrypt
3.搜索关键字encrypt通过断点调试查看到内部真实数据
client_id=c3cef7c66a1843f8b3a9e6a1e3160e20&grant_type=password×tamp=1566371889615&source=com.zhihu.web&signature=849409fe69f76b28a7ebfa95f0acc784d7c812bf&username=+8618896530856&password=dadasdasdas&captcha=nngt&lang=en&utm_source=&ref_source=other_https://www.zhihu.com/signin?next=%2F