- requests网页下载库
- URL管理器
- HTML简介
- 网页解析器 beautiful soup
requests网页下载库
发送requests请求
reguests.get/post(url,params,data,headers,timeout,verify,allow redirects,cookies)
url
:要下载的目标网页的URLparams
:字典形式,设置URL后面的参数,比如?id=123&name=xiaomingdata
:字典或者字符串,一般用于POST方法时提交数据headers
:设置user-agent、refer等请求头timeout
:超时时间,单位是秒verify
:True/False,是否进行HTTPS证书验证,默认是,需要自己设置证书地址allow_redirects
:True/False是否让requests做重定向处理,默认是cookies
:附带本地的cookies数据
接受response响应
requests.get/post(url)
r.status_code
:查看状态码,如果等于200代表请求成功r.encoding
:可以查看当前编码,以及变更编码(重要!requests会根据Headers推测编码,推测不到则设置为ISO-8859-1可能导致乱码)r.text
查看返回的网页内容r.headers
:查看返回的HTTP的headersr.url
:查看实际访问的URLr.content
:以字节的方式返回内容,比如用于下载图片r.cookies
:服务端要写入本地的cookies数据
注意事项
如果headers没有encoding信息,默认ISO-8859-1
中文网页可能出现乱码,查看text,在<header>
标签中<meta \>
的charset
成员可能含有编码信息
可自行设定r.encoding
成员
URL管理器
作用:对爬取URL进行管理,防止重复和循环爬取,支持新增URL和取出URL
见utils/url_manager.py
class UrlManager:
"""
URL管理器
"""
def __init__(self) -> None:
self.newUrls = set()
self.oldUrls = set()
pass
def AddUrl(self, url):
if url is None or len(url) == 0:
return # 不合格
# 判断url是否已经存在
if url in self.newUrls or url in self.oldUrls:
return
self.newUrls.add(url)
def AddUrls(self, urls):
if urls is None or len(urls) == 0:
return
for url in urls:
self.AddUrl(url)
pass
def GetUrl(self):
if self.HasNewUrl():
url = self.newUrls.pop()
self.oldUrls.add(url)
return url
else:
return None
def HasNewUrl(self):
return len(self.newUrls) > 0
HTML简介
网页解析器 beautiful soup
语法
见myBS.py
BeautifulSoup方法选择器find()方的使用
关于find()函数参数的一些tips
- 可以抓取存在某种标签的情形和存在标签匹配的情形
items = soup.find_all("div","class")
- 可以部分匹配
<div>
标签们中有两个数据<div class="craw_me marisa">
和<div class="craw_me reimu">
执行
后,输出列表items = soup.find_all("div", class_="craw_me") print(items)
[<div class="craw_me reimu">you craw reimu </div>, <div class="craw_me marisa">you craw marisa </div>]
。即find()可以部分匹配
beautifulsoup获取有价值的信息
- view-source,未加载动态之前的源代码
- 右键-检查
Elements
->浏览器展示真实看到的代码,如已经加载js
Network
抓包- preserve log 跳转到新页面时保存原来log
- disable cache 防止从本地取数据
Doc: Headers的一些重要参数
Request Method
Get/Post
Status Code
200请求成功
Content-Type
返回,可能是text/html 可能是json等。以及有编码如UTF-8
Cookie
User-Agent
实战
爬取静态网站
爬取epicmo的博文标题
blogtest.py
爬去文章博客全部文章列表
requests请求时附带cookie字典
import requests
cookies = {
"captchakey":"14a54079a1",
"captchaExpire":"1548852352"
}
r = requests.get(url, cookies=cookies)
正则表达式实现模糊匹配
r/R:非转义的原始字符串
与普通字符相比,其他相对特殊的字符,其中可能包含转义字符,即那些,反斜杠加上对应字母,表示对应的特殊含义的,比如最常见的”\n”表示换行,”\t”表示Tab等。而如果是以r开头,那么说明后面的字符,都是普通的字符了,即如果是“\n”那么表示一个反斜杠字符,一个字母n,而不是表示换行了。
以r开头的字符,常用于正则表达式,对应着re模块。
版权声明:本文为CSDN博主「抖腿大刘」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u010496169/article/details/70045895
url1 ="http://www.crazyant.net/123.html"
url2 ="http://www.crazyant.net/123.html#comments
url3 ="http://www.baidu.com'
import re
pattern = r'^http://www.crazyant.net/\d+\.html$'
print(re.match(pattern, url1)) # ok
print(re.match(pattern,url2)) # None
print(re.match(pattern,url3)) # None
filmreview
从url https://movie.douban.com/review/best/ 中获取热门评论共5页,包括评价电影,时间,文本内容。并将结果存在数据库中。
在这个案例中着重使用了user-agent简单破解网站的反爬机制,以及使用多个find来定位所需要的数据。以及一些sql的小知识。
北京天气
https://tianqi.2345.com/wea_history/54511.htm
涉及技术
- headers中设置user agent反爬机制
- 通过network抓包,payload中有所传参数,分析ajax的请求和参数
- 通过for循环请求不同的参数的数据
- 利用pandas实现excel的合并与保存
[pre]beautifulsoup抓取动态网页
(https://blog.51cto.com/u_15057841/3460666)
selenium实现关于动态网页的抓取
前提,selenium教程
本文过时信息注意
python3.0以后 selenuim.webdriver 库不在推荐使用find_element_by_接后缀的方法
现在使用find_element和find_elements方法,其中
-
通过webdriver对象的find_element(“属性名”,“属性值”)
- 我们要定位一个属性id,值为"wang"的元素
find_element('id','wang')
- 我们要定位一个属性class,值为"plant"的元素
find_element('id',"plant")
-
通过webdriver模块中的By,以指定方式定位元
导入模块:from selenium.webdriver.common.by import By
如:定位id为username,class_name为password,tag_name为input的元素from selenium.webdriver.common.by import By webdriver.find_element(By.ID,'username') webdriver.find_element(By.CLASS_NAME,'password') webdriver.find_element(By.TAG_NAME,'input')
其实这种方法和第一种方法类似,因为By类中的ID,CLASS_NAME这些成员都是常量字符串,值就是’id’,‘class name’
-
find_element()
和find_elements()
的区别
find_element()
的返回结果是一个WebElement对象,如果符合条件的有多个,默认返回找到的第一个,如果没有找到则抛出NoSuchElementException异常。find_elements()
的返回结果是一个包含所有符合条件的WebElement对象的列表,如果未找到,则返回一个空列表。
如果要做到像soup那样的键值对部分匹配,需要用到xpath
定位
xpath常用表达式
表达式 | 描述 | 举例 |
---|---|---|
nodename | 选取此节点的所有子节点 | form ,选取form节点 |
/ | 从根节点开始选取,绝对定位 | /html/body/form/input ,选取input节点 |
// | 从符合条件的元素的开始,而不考虑它们的位置。相对定位 | //form/input ,选取input节点form//input ,选择form元素的后代的所有 input 元素 |
. | 选取当前节点 | |
… | 选取当前节点的父节点 | //input/.. |
@ | 元素属性 | //form/input[@name] ,选取带有name属性的input节点;//input[@name=‘username’] ,选取所有name属性为username的input节点;//input[@*] 选择有任何属性的input节点 |
[] | 如果有多个元素,可以进行筛选 | /div[1] 第一个(和传统排序方法不同);/div[last()] 最后一个;/div[last()-1]倒数第二个 |
* | 选择任何节点 | /form/* ,选择form之后的所有节点 |
| | 或者 | //form | //a ,选择所有的input和a节点 |
@属性名 | 选取属性 | //a[@id=“link1”]/@href ,选取id为link1的a标签的href属性值(此方法在lxml可用) |
text() | 获取文本内容 | //a[@id=“link1”]/text() ,选取id为link1的a标签的文本内容(此方法在lxml可用) |
contains() | 包含内容(模糊匹配) | //span[contains(text(), “Latest”)] ,选取文本内容包含“Latest”的span标签 |
css常用表达式
表达式 | 说明 | 举例 |
---|---|---|
#id | 通过id选择元素 | #username,选择id为username的元素 |
.class | 通过类选择元素 | .container ,选择class为container的元素 |
element | 通过元素名选择元素 | input,选择所有input元素 |
[attribute] | 通过属性选择元素,选择具有attribute属性的元素 | |
[attribute=value] | 通过属性选择元素 | [type=“password”],选择type属性为password的元素 |
a:link,a:visited | 选择未被访问、已被访问过的元素 | |
p:empty | 选择没有子元素的p元素 | |
element>element | 选择父元素为 div 的所有input 元素 | div>input |
表达式实例
拿test.html
举例,
# 绝对路径(层级关系)定位
# soup.find_all(xxx)[n].find(xxx)
data = edge.find_elements("xpath","/html/body/div")
data = edge.find_elements("xpath","/html/body/div[1]")
data = edge.find_elements(By.CSS_SELECTOR, "html>body>div") # 这种方法只能返回第一或者一个列表而不能div[2]这种返回指定序号
执行命令,可以得到一个列表,而在列表[0]又可以find_element
此外有意思的是
[0].text
‘段落\n百度\n疯狂的蚂蚁\n爱奇艺’
# 利用元素属性定位
# soup.find_all(class_=["xxx",...])
data = edge.find_elements(By.XPATH,"//div") # 返回列表
# 层级+元素属性定位
# soup.find_all("div",class_=["xxx",...])
data = edge.find_elements(By.XPATH,"//div[@nb='233']") # 完全匹配,返回列表,两个元素
data = edge.find_elements(By.XPATH, "//div[@class='craw_me reimu']") # 完全匹配,唯一
data = edge.find_elements(By.XPATH, "//div[contains(@class,'craw_me')]") # 部分包含
data = edge.find_elements(By.CSS_SELECTOR, "html>body>div[nb='233']") # 完全匹配,返回列表,两个元素
# 逻辑运算符定位
data = edge.find_elements(By.XPATH, "//div[contains(@class,'craw_me') and contains(@nb,'233')]")
表达式定位总结
XPath常用的定位方式:
-
元素属性,快速定位,唯一属性:
//*[@id="images"]
-
层级与属性结合,解决没有属性问题:
//div[@id="images"]/a[1]
-
属性与逻辑结合,解决多个属性重名问题:
//*[@id="su" and @class="bg s_btn" ]
注意,表达式里的下标是从1开始的。
绝对定位以/开头,依赖页面的元素的顺序和位置,相对定位以//开头,不依赖页面元素顺序和位置,根据条件进行匹配,优先使用相对定位。
学习XPath本质就是掌握各种表达式的技巧,除了上述说到方法外,还有一些特别的定位方式:
-
查找id属性的值包含"kw"的元素:
//*[contains(@id,'kw')]
-
查找⽂本⾥包含"新闻"的元素:
//*[contains(text(),'新闻')]
-
查找class属性中开始位置包含’s_form_wrapper’关键字的元素:
//*[starts-with(@class,'s_form_wrapper')]
-
使⽤多个相对路径去定位⼀个元素⽤//分开:
//div[@class=‘formgroup’]//input[@id=‘user-message’]
-
轴定位:
轴定位,使用::表示
查找id="head"元素后⾯标签名为input的第一个元素
//*[@id="head"]//following::input[1]
其次link定位和partial-link定位对于匹配text的效果也非常好,这里截取原blog相关内容
link 专门用来定位文本链接,假如要定位下面这一标签。
<div class="practice-box" data-v-04f46969="">加入!每日一练</div>
我们使用find_element_by_link_text
并指明标签内全部文本即可定位。
driver.find_element_by_link_text("加入!每日一练")
partial_link 翻译过来就是“部分链接”,对于有些文本很长,这时候就可以只指定部分文本即可定位,同样使用刚才的例子。
<div class="practice-box" data-v-04f46969="">加入!每日一练</div>
我们使用 find_element_by_partial_link_text
并指明标签内部分文本进行定位。
driver.find_element_by_partial_link_text("加入")
当然由于方法的过期,以上方法改为find_element(By.LINK_TEXT,“xxx”)
selenium搭配beautifulsoup
由于selenium的find方法不太熟悉,这里搭配beautifulsoup
selenium搭配beautifulsoup
版权声明:本文为CSDN博主「Java Punk」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
-
导入关键库
import time # time函数 import xlwt # 进行excel操作 import os.path # os读写 from bs4 import BeautifulSoup # 解析html的 from selenium import webdriver # selenium 驱动 from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys # 模仿键盘 from selenium.webdriver.support.wait import WebDriverWait # 导入等待类 from selenium.webdriver.support import expected_conditions as EC # 等待条件
-
Selenium 解析动态Html
原博客中采用了判断当前页面没有明显的元素用来判断拉到什么位置就是底部,所以我的规则是:一直循环,直到连续5次
Keys.PAGE_DOWN
(下拉),<li>
标签数量不再增加,就认为已经到底了。为了避免程序计算太快,每次下拉还停顿了0.2秒,实际效果不错。 -
Selenium 转 BeautifulSoup
根据小编的开发经验,selenium 很擅长模拟和测试,它动态加载的特性是 BeautifulSoup不 具备的。但是,对于取值操作,简单的还好,复杂点的比如:循环
- 标签这种操作,我还是觉得BeautifulSoup更方便。
在爬虫的世界里,大量有价值的数据都是循环展现的,比如:某排行榜,某商品列表等…所以,Selenium + BeautifulSoup的操作必不可少。
核心代码也非常简单,直接传入 Selenium 驱动 driver,用 page_source() 就可以啦。# 获取完整渲染的网页源代码 pageSource = driver.page_source soup = BeautifulSoup(pageSource, 'html.parser') soup.prettify()
- 标签这种操作,我还是觉得BeautifulSoup更方便。
-
保存数据至excel
def saveData(datalist, savepath): print("—————————— save ——————————") book = xlwt.Workbook(encoding="utf-8",style_compression=0) # 创建workbook对象 sheet = book.add_sheet(dataTime, cell_overwrite_ok=True) # 创建工作表。sheet页名为dataTime col = ("排名", "图片链接", "名称", "品牌", "指导价", "销量") for i in range(0, len(col)): sheet.write(0, i, col[i]) # 列名 for i in range(0, len(datalist)): # print("第%d条" %(i+1)) # 输出语句,用来测试 data = datalist[i] for j in range(0, len(col)): sheet.write(i+1, j, data[j]) # 数据 if os.path.exists(savepath): # 清空路径 os.remove(savepath) book.save(savepath) # 保存
另外,在做爬虫时,通常是不需要打开浏览器的,只需要使用浏览器的内核,因此可以使用Chrome的无头模式
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
option = webdriver.EdgeOptions()
option.add_experimental_option("detach", True)
option.add_argument("--headless")
浏览器窗口切换
作者:Dream丶Killer
链接:https://juejin.cn/post/7074779332819812389
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
在很多时候我们都需要用到窗口切换,比如:当我们点击注册按钮时,它一般会打开一个新的标签页,但实际上代码并没有切换到最新页面中,这时你如果要定位注册页面的标签就会发现定位不到,这时就需要将实际窗口切换到最新打开的那个窗口。我们先获取当前各个窗口的句柄,这些信息的保存顺序是按照时间来的,最新打开的窗口放在数组的末尾,这时我们就可以定位到最新打开的那个窗口了。
# 获取打开的多个窗口句柄
windows = driver.window_handles
# 切换到当前最新打开的窗口
driver.switch_to.window(windows[-1])
方法 | 描述 |
---|---|
send_keys() | 模拟输入指定内容 |
clear() | 清除文本内容 |
is_displayed() | 判断该元素是否可见 |
get_attribute() | 获取标签属性值 |
size | 返回元素的尺寸 |
text | 返回元素文本 |
鼠标和键盘
作者:Dream丶Killer
链接:https://juejin.cn/post/7074779332819812389
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
在webdriver 中,鼠标操作都封装在ActionChains类中,常见方法如下:
方法 | 描述 |
---|---|
click() | 单击左键 |
context_click() | 单击右键 |
double_click() | 双击 |
drag_and_drop() | 拖动 |
move_to_element() | 鼠标悬停 |
perform() | 执行所有ActionChains中存储的动作 |
单击左键
模拟完成单击鼠标左键的操作,一般点击进入子页面等会用到,左键不需要用到 ActionChains 。
# 定位搜索按钮
button = driver.find_element_by_xpath('//*[@id="toolbar-search-button"]/span')
# 执行单击操作
button.click()
单击右键
鼠标右击的操作与左击有很大不同,需要使用 ActionChains 。
from selenium.webdriver.common.action_chains import ActionChains
# 定位搜索按钮
button = driver.find_element_by_xpath('//*[@id="toolbar-search-button"]/span')
# 右键搜索按钮
ActionChains(driver).context_click(button).perform()
双击
模拟鼠标双击操作。
# 定位搜索按钮
button = driver.find_element_by_xpath('//*[@id="toolbar-search-button"]/span')
# 执行双击动作
ActionChains(driver).double_click(button).perform()
拖动
模拟鼠标拖动操作,该操作有两个必要参数,
- source:鼠标拖动的元素
- target:鼠标拖至并释放的目标元素
# 定位要拖动的元素
source = driver.find_element_by_xpath('xxx')
# 定位目标元素
target = driver.find_element_by_xpath('xxx')
# 执行拖动动作
ActionChains(driver).drag_and_drop(source, target).perform()
鼠标悬停
模拟悬停的作用一般是为了显示隐藏的下拉框,比如 CSDN 主页的收藏栏,我们看一下效果。
# 定位收藏栏
collect = driver.find_element_by_xpath('//*[@id="csdn-toolbar"]/div/div/div[3]/div/div[3]/a')
# 悬停至收藏标签处
ActionChains(driver).move_to_element(collect).perform()
键盘控制
webdriver 中 Keys 类几乎提供了键盘上的所有按键方法,我们可以使用 send_keys + Keys 实现输出键盘上的组合按键如 “Ctrl + C”、“Ctrl + V” 等。
from selenium.webdriver.common.keys import Keys
# 定位输入框并输入文本
driver.find_element_by_id('xxx').send_keys('Dream丶killer')
# 模拟回车键进行跳转(输入内容后)
driver.find_element_by_id('xxx').send_keys(Keys.ENTER)
# 使用 Backspace 来删除一个字符
driver.find_element_by_id('xxx').send_keys(Keys.BACK_SPACE)
# Ctrl + A 全选输入框中内容
driver.find_element_by_id('xxx').send_keys(Keys.CONTROL, 'a')
# Ctrl + C 复制输入框中内容
driver.find_element_by_id('xxx').send_keys(Keys.CONTROL, 'c')
# Ctrl + V 粘贴输入框中内容
driver.find_element_by_id('xxx').send_keys(Keys.CONTROL, 'v')
其他常见键盘操作:
操作 | 描述 |
---|---|
Keys.F1 | F1键 |
Keys.SPACE | 空格 |
Keys.TAB | Tab键 |
Keys.ESCAPE | ESC键 |
Keys.ALT | Alt键 |
Keys.SHIFT | Shift键 |
Keys.ARROW_DOWN | 向下箭头 |
Keys.ARROW_LEFT | 向左箭头 |
Keys.ARROW_RIGHT | 向右箭头 |
Keys.ARROW_UP | 向上箭头 |
[pre]元素等待
[pre]弹窗处理
cookies操作
作者:Dream丶Killer
链接:https://juejin.cn/post/7074779332819812389
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
cookies 是识别用户登录与否的关键,爬虫中常常使用 selenium + requests 实现 cookie 持久化,即先用 selenium 模拟登陆获取 cookie ,再通过 requests 携带 cookie 进行请求。
webdriver 提供 cookies 的几种操作:读取、添加删除。
- get_cookies:以字典的形式返回当前会话中可见的 cookie 信息。
- get_cookie(name):返回 cookie 字典中 key == name 的 cookie 信息。
- add_cookie(cookie_dict):将 cookie 添加到当前会话中
- delete_cookie(name):删除指定名称的单个 cookie。
- delete_all_cookies():删除会话范围内的所有 cookie。
下面看一下简单的示例,演示了它们的用法。
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://blog.csdn.net/")
# 输出所有cookie信息
print(driver.get_cookies())
cookie_dict = {
'domain': '.csdn.net',
'expiry': 1664765502,
'httpOnly': False,
'name': 'test',
'path': '/',
'secure': True,
'value': 'null'
}
# 添加cookie
driver.add_cookie(cookie_dict)
# 显示 name = 'test' 的cookie信息
print(driver.get_cookie('test'))
# 删除 name = 'test' 的cookie信息
driver.delete_cookie('test')
# 删除当前会话中的所有cookie
driver.delete_all_cookies()
其他参考教程
Python爬虫库xPath, BeautifulSoup, re, selenium的详细用法
实战:爬取pixiv某画师的所有图片
涉及技术:反爬,使用第三方反向代理,二进制读取,二进制存储
Python爬虫之如何玩转cookie(技巧篇)
soup = BeautifulSoup(html_doc, "html.parser")
回复删除