爬虫总结
目录
- 爬虫总结
-
- 一、静态页面 html 代码的获取
-
- 1. 请求数据
-
- ① requests
- ② urllib & urllib3
- ③ selenium (webdriver)
- 2. 节点获取 / 内容匹配
-
- ① re
-
- 1. 语法
- 2. 实战
- ② bs4
-
- 1. 语法
- 2. 实战
- ③ xpath
-
- 1. 语法
- 2. 实战
- 二、多线程 和 线程池
-
- 1. 多线程
- 2. 线程池
- 3. 线程实战
- 三、保存数据
-
- 1. 保存到 Excel xls (xlwt)
- 2. 保存到 数据库 db (sqlite3)
- 3. 获取爬虫数据的问题
- 四、获取动态数据的操作
-
- 1. selenium webdriver驱动
- 2. selenium 匹配节点及其内容
- 3. selenium 执行js操作浏览器
-
- 常用的js代码
- 4. selenium 常用的浏览器方法
-
- ① 获取本页面URL
- ② 获取日志
- ③ 窗口操作
- ④ 设置延时
- ⑤ 关闭
- ⑥ 打印网页源代码
- ⑦ 屏幕截图操作
- ⑧ 前进后退刷新
- ⑨ 执行JS代码
- ⑩ Cookies操作
- ? 获取标题内容
- ? 获取当前浏览器名称
- ? 全局超时
- 5. selenium 常用的元素操作方法
- 6. 键盘鼠标操作 (动作API)
-
- ① 鼠标操作
- ② 键盘操作
- ③ 其他操作
- 7. 选项卡管理
- 8. 切换 Frame
- 9. Alert
-
- ② 弹出框的浏览器
- ② 新窗口弹出框
- ③ 人为弹出框
- 10. 判断
- 11. 下拉选择框
- 12. 延时等待
一、静态页面 html 代码的获取
1. 请求数据
① requests
url = 'http://www.baidu.com' # 这里不要用https resp = requests.get(url) # 请求数据 resp = requests.post(url=url, data=data) # post方法请求数据(只需添加字典类型)data即可) resp.encoding = 'utf-8' # 根据html的charset设置编码方法可以避免中文乱码 print(resp) # <Response [200]> -- 是一个Request对象包括响应头,html之类的 # ★ 获取网页源代码(html) html = resp.text # 获取网页源代码(html)、数据 --- str类型 html = resp.content # 获取网页源代码(html)、数据 --- bytes类型 (下载图片可以如此)
② urllib & urllib3
import urllib url = # 一、简单获取一个get请求 (不修改headers) resp = urllib.request.urlopen(url) # 二、简单获取一个post请求 (不修改headers) # Post请求需要向服务器发送表单内容,用urlopen中的data参数 # data 是 bytes 字节型数据,需要用 bytes() 转换成字节型 # 如果data被赋值,则请求的方式就会由get转为post,而post需要提供一些待处理的数据data # 这些待处理的数据需要一定的格式,因此就需要 urllib.parse.urlencode() 编码 # 【注】不能对string编码,只能对dict类型编码 import urllib.parse data = bytes(urllib.parse.urlencode({
'name': 'Syy'}), encoding='utf-8') resp = urllib.request.urlopen(url=url, data=data) # ★ 拿到网页源代码(html) # 拿到网页源代码(html)、数据 --- str类型 html = resp.read().decode('utf-8') # 以utf-8的方式对获取到的网页源码进行解码,可以更好的解析网页,解析换行符、中文等 html = resp.read() # 拿到网页源代码(html)、数据 --- bytes类型 # 三、拿到请求头的一些属性值 print(resp.status) # 200 (状态码) # 查看所有属性值(有s) print(resp.getheaders()) # 查看所有属性值(无s) print(resp.getheader('Server')) # 四、伪装成浏览器(将User-Agent由爬虫改为浏览器的信息) # ① Post 请求 headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36' } data = bytes(urllib.parse.urlencode({
'name': 'Eric'}), encoding='utf-8') # ⭕构建(封装)一个请求对象 req = urllib.request.Request(url=url, headers=headers, data=data, method='POST') resp = urllib.request.urlopen(req) # ② Get请求 headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36' } # 构建(封装)一个请求对象 req = urllib.request.Request(url=url, headers=headers) resp = urllib.request.urlopen(req)
import urllib3
url = 'http://www.baidu.com'
http = urllib3.PoolManager(n) # 创建连接池管理对象,参数可填几个,可以实现向多个服务器发送请求
resp = http.request(method='get',url=url)
resp = http.urlopen(method='get',url=url) # urlopen 跟 request方法效果一样,但无法直接添加headers,因此用的少
print(resp) # <urllib3.response.HTTPResponse object at 0x0000014DFF9459A0> 是一个响应对象
# ★ 拿到网页源代码(html)
html = resp.data.decode('utf-8') # 拿到网页源代码(html)、数据 --- str类型
html = resp.data # 拿到网页源代码(html)、数据 --- bytes类型 (下载图片可以如此)
③ selenium (webdriver)
from selenium import webdriver
# (注意:驱动需要与浏览器版本兼容)
# 配置Chrome启动时属性的类ChromeOptions
options = webdriver.ChromeOptions()
options.add_argument('--headless') # 设置 无界面浏览 # 还可以设置UA、IP代理等等
driver = webdriver.Chrome(options=options) # 创建了 Chrome 实例
driver.get('http://www.baidu.com') # 发送请求,请求数据
html = driver.page_source # 拿到网页源代码(html)、数据 --- str类型
2. 节点获取 / 内容匹配
① re
1. 语法
''' 正则:用来匹配字符串的一门表达式语言 测试的方法:https://tool.oschina.net/regex/ 1.正则支持所有普通字符 2.元字符,就一个符号来匹配一堆内容 \d --能够匹配一个数字(0-9) \D --匹配除了数字(0-9)的内容 \w --能够匹配数字、字母、下划线(0-9,a-z,A-Z,_) \W --除了数字、字母、下划线(0-9,a-z,A-Z,_)以外的内容 [abc] -- 匹配字母 a 或 b 或 c 的内容 [^abc] -- 匹配除了字母 a 或 b 或 c 的内容 . -- 匹配 除了换行符以外 的所有内容 3.量词,控制前面元字符出现的频次 + -- 前面的元字符出现 1 次或多次 * -- 前面的元字符出现 0 次或多次,贪婪匹配的,尽可能多的拿到数据 ? -- 前面的元字符出现 0 次或 1 次 4.惰性匹配 : .*? 例如: 玩儿吃鸡游戏,晚上一起打游戏,干嘛呢?打游戏啊! 正则:玩儿.*游戏 .* -- 找最远的"游戏" (即 -- 中间尽可能多) 结果:玩儿吃鸡游戏,晚上一起打游戏,干嘛呢?打游戏 正则:玩儿.*?游戏 .*? -- 找最近的"游戏" (即 -- 中间尽可能少) 结果:玩儿吃鸡游戏 😊.*? : 惰性匹配 xxx.*?xx-- 匹配xxx 到最近的xx为止内容,😊 '''
''' re.findall(r'',str) # 匹配正则表达式匹配的内容 r''--原始字符串,避免转义字符的影响 返回一个装有匹配结果的列表 re.search(r'',str) # 只拿到第一个结果就返回 re.finditer(r'',str) # 匹配正则表达式匹配的内容,返回的是一个迭代器对象 # findall 和 finditer 的结果效果相同,选择哪个主要看获取的内容大小,如果太大,选择finditer比较合适,占用内存少 # 结合写法: re.findall(re.compile(r''),str) # 预加载 # 提前准备好一个正则对象 # re.compile 用于编译正则表达式,生成一个 Pattern 对象 # 此对象可以使用 findall、finditer、search等等各种re方法 obj = re.compile(r'') obj.search(str) obj.finditer(str) obj.findall(str) (?P<组名>正则表达式) -- ?P表示要分组,<>内为组名 item.group() # 打印当前匹配到的内容(只能用于 re.search和 re.finditer 的结果内容的遍历) item.group('组名') # 打印当前匹配到的指定组的内容 # 必须在设定了组名的时候才能使用 item.groupdict() # 必须在设定了组名的时候才能使用 '''
# 一、findall()
result = re.findall(r'\d+', '今天我有100块,买了2块蛋糕') # 匹配正则表达式匹配的内容 r''--原始字符串,避免转义字符的影响 返回一个装有匹配结果的列表
print(result) # ['100', '2']
''' 注意:search 和 finditer中,()括起来的内容是分组内容,是要输出的内容,不用括号括起来的内容至会匹配不会输出! '''
# 二、search()
result = re.search(r'(?P<text>\d+)', '今天我有100块,买了2块蛋糕') # 只拿到第一个结果就返回
print(result.group()) # 输出所有分组内容,即()括起来的内容
print(result.group('text')) # 分组起名后可以按名称查询
# 三、finditer()
result = re.finditer(r'\d+', '今天我有100块,买了2块蛋糕') # 匹配正则表达式匹配的内容,返回的是一个迭代器对象
for item in result:
print(item.group())
# result = re.finditer(r'(?P<text>\d+)', '今天我有100块,买了2块蛋糕') # 匹配正则表达式匹配的内容,返回的是一个迭代器对象
print(item.group('text')) # 分组起名后可以按名称查询
print(item.groupdict()) # 字典格式打印所有匹配的组
# 四、sub()
# re.sub() 利用正则来实现字符替换
print(re.sub('a','A', 'abcdefghijklmn')) # 找到a用A替换
# 建议在正则表达式中,在被比较的字符串前面加上r,这样不用担心转义字符的问题
print(re.sub('a','A', r'\abcdefgh\ijklmn\l'))
# ★ 例子2:预加载
# 提前准备好一个正则对象
# re.compile 用于编译正则表达式,生成一个 Pattern 对象
obj = re.compile(r'\d+') # 此对象可以使用 findall、finditer、search等等各种re方法
result = obj.findall('今天我有100块,买了2块蛋糕')
obj.search('今天我有100块,买了2块蛋糕')
obj.finditer('今天我有100块,买了2块蛋糕')
print(result)
2. 实战
# 测试正则表达式用于爬虫
html = requests.get(url).text
html = ''' <div class="abc"> <div><a href="baidu.com">我是百度</div> <div><a href="qq.com">我是腾讯</div> <div><a href="162.com">我是网易</div> </div> '''
# ★ finditer 方法
obj = re.compile(r'<div><a href="(?P<url>.*?)">(?P<text>.*?)</div>')
result = obj.finditer(html)
for item in result:
print(item.group('url'), end=' ')
print(item.group('text'))
print(item.groupdict()) # 字典格式打印所有匹配的组
''' baidu.com 我是百度 qq.com 我是腾讯 162.com 我是网易 {'url': '162.com', 'text': '我是网易'} '''
# ★ findall 方法
obj = re.compile(r'<div><a href="(.*?)">(.*?)</div>')
result = obj.findall(html) # result是一个列表
print(result[0]) # 可以直接输出
# findall 和 finditer 的结果效果相同,选择哪个主要看获取的内容大小,如果太大,选择finditer比较合适,占用内存少
# ★ search 方法
obj = re.compile(r'<div><a href="(?P<url>.*?)">(?P<text>.*?)</div>')
result = obj.search(html)
print(item.group('url'), end=' ')
print(item.group('text'))
print(item.groupdict()) # 字典格式打印所有匹配的组
② bs4
1. 语法
''' BeautifulSoup4将复杂html文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: - Tag - NavigableString - BeautifulSoup - Comment ''' from bs4 import BeautifulSoup html = requests.get(url).text # 解析内容:html,用的解析器:html.parser bs = BeautifulSoup(html, 'html.parser') # 1.拿到整个文档(html) print(bs) print(type(bs)) # <class 'bs4.BeautifulSoup'> # 2.Tag --
标签及其内容:找到的第一个标签及其内容 # bs.标签名 # 找到第一个出现的标签及其所有内容 print(bs.title) # <title>百度一下,你就知道 </title> print(bs.a) # print(bs.div) print(type(bs.title)) # <class 'bs4.element.Tag'> # 3.NavigableString --标签里的内容(字符串) # bs.标签名.string print(bs.title.string) # 百度一下,你就知道 # 4.拿到一个标签的所有属性 # bs.标签名.attrs print(bs.a.attrs) # 5. Comment --注释类型,是一种特殊的NavigatableString,输出的内容不包含注释符号 print(bs.a.string) # 新闻 print(type(bs.a.string)) # <class 'bs4.element.Comment'>
# 解析内容:html,用的解析器:html.parser
bs = BeautifulSoup(html, 'html.parser')
# 【节点获取】
# 一、文档的遍历
# contents -- 返回一个列表,里面装的是标签中的内容
print(bs.div.contents)
print(bs.div.contents[0])
print(bs.div.contents[1])
print(type(bs.div.contents[1])) # <class 'bs4.element.Tag'>
# 二、 文档的搜索 (更重要)
# 1.find_all() --查询所有指定节点
# ① 字符串过滤:会查找所有与字符串完全匹配的节点内容
t_list = bs.find_all('a') # ★ 查找所有a标签
print(t_list)
# ② 正则表达式
# 正则表达式搜索,使用search()方法来匹配内容
t_list = bs.find_all(re.compile('a')) # ★ 查找所有带有'a'字符的节点
print(t_list)
# ③ 方法:传入一个函数:根据函数的要求来搜索
def name_is_exists(tag):
# 标签中有name属性才会筛选出来(拿到)
return tag.has_attr('name')
t_list = bs.find_all(name_is_exists) # 这里只需要写函数名
print(t_list)
# 2.kwargs 参数
# 指定一些参数搜索:class_、id...
t_list = bs.find_all(id='head')
# html的class属性要加个下划线_,为了和Python的内部class区分开来!
t_list = bs.find_all(class_=True) # 这里是指有class属性的标签,而不是class=True的标签
t_list = bs.find_all(class_="mnav") # 这里是指class="mnav"的标签
t_list = bs.find_all(href="http://news.baidu.com")
for item in t_list:
print(item)
# 3.text 参数
# 查找文本
t_list = bs.find_all(text='hao123')
t_list = bs.find_all(text=['hao123', '百度', '贴吧'])
t_list = bs.find_all(text=re.compile('\d')) # 应用正则表达式查找含有特定文本的内容(标签里的字符串) # ['hao123']
for item in t_list:
print(item)
# 4.limit 参数
t_list = bs.find_all('a', limit=3) # 查找前 3 个 a标签
print(t_list)
# 5.指定属性查找
# ★ 既是某个标签,又有某种属性
t_list = bs.find_all('a', class_='mnav')
t_list = bs.find_all('a', {
'class': 'mnav', 'id': '', 'name': 'tj_trnews'}) # 获取带有这些属性的<a>标签
print(t_list)
# ★【css选择器】
t_list = bs.select('title') # 通过标签来查找
t_list = bs.select('.mnav') # 通过标class来查找
t_list = bs.select('#u1') # 通过id来查找
t_list = bs.select('a[class="bri"]') # 通过属性值来查找
t_list = bs.select('head > title') # 通过子代选择器来查找
t_list = bs.select('.mnav ~ .bri') # 兄弟选择器
print(t_list[0].get_text()) # 获取文本
2. 实战
# 2.逐一解析数据
soup = BeautifulSoup(html, "html.parser")
# soup.find_all('div', class_='item') # 查找指定class='item'的div节点
for item in soup.find_all('div', class_='item'): # 带有 class = item 的div
# print(item) # 测试查看item(筛选后的html内容)
print(type(item)) # <class 'bs4.element.Tag'>
item = str(item) # 转换成字符串str,方便后面用正则表达式
③ xpath
1. 语法
正则表达式 re模块 适合一长段凌乱的字符串的匹配
xpah lxml-->etree 适合层次鲜明的节点的层层获取
''' xpath解析 xpath 是针对 xml 创建的一种表达式语言,可以从 xml 中直接提取数据 html 是 xml 的子集,因此 xpath 也可以处理 html '''
''' from lxml import etree 从包中导入模块 # etree.HTML 用来解析字符串格式的HTML文档对象,将传进去的字符串转变成_Element对象。 # 作为_Element对象,可以方便的使用getparent()、remove()、xpath()等方法。 et = etree.HTML(html) -- 加载数据,返回element对象 et.xpath('') -- 匹配节点 /html/body/div/span/text() -- text() : 拿到标签内的文本 /html/body/div/span/@属性 -- @属性 : 拿到某个属性值 /html/*/span/@属性 -- * 通配符,任意的字符,啥都行 et.xpath('//a/@href') -- // 表示从任意位置开始找 et.xpath('//span[@class="title"]/text()') -- [@属性名xxx=属性值xx] 表示属性上的限定 href = item.xpath('./a/@href')[0] -- ./表示当前这个元素 补充: ① 标签同时具有两个属性值,使用and连接即可 '//div[@class="icon" and @id="next"]/@href' 同一个属性有两个值,可以用contains(@属性名=属性值)来限定 .xpath('//sdiv[@id="a" and contains(@class,"b") and contains(@class,"c")]/text() ② XPath常用规则 / (直接)子节点 // 所有子孙节点 (后代) . 选取当前节点 .. 表示父节点 [1] 表示第一个某元素 -- //div[1] 第一个div元素 [last()] 表示最后一个 -- //div[last()] 最后一个dv | //div[last() - 1] 倒数第二个dv [position() < 3] 前两个某元素 -- //div[position() < 5] 前4个div contains(@属性名,属性值) 某元素的某个属性中包含某个值 -- div[contains(@class,'abc')]class中含有'abc'的div starts-with(@属性名,属性值) 选择以 某个属性名=xxx 开头的元素 '''
from lxml import etree
# ① 加载准备解析的数据 (爬取数据)
f = open('text.html', mode='r', encoding='utf-8')
html = f.read()
print(html) # 拿到了html
# ② 加载数据,返回element对象
# etree.HTML 用来解析字符串格式的HTML文档对象,将传进去的字符串转变成_Element对象。
# 作为_Element对象,可以方便的使用getparent()、remove()、xpath()等方法。
et = etree.HTML(html) # et是一个element对象
# xpath的语法
# 不用//,就必须从/html开始
result = et.xpath('/html') # /html表示根节点
result = et.xpath('/html/body') # 表达式中间的/表示一层html节点
result = et.xpath('/html/body/div/span')
result = et.xpath('/html/body/div/span/text()') # text()表示提取标签中的文本信息
result = et.xpath('/html/body/div/*/li/a/text()') # * 通配符,任意的字符,啥都行
result = et.xpath('/html/body/div/*/li/a/@href') # @表示属性
print(type(result)) # <class 'list'>
print(type(result[0])) # <class 'lxml.etree._Element'>
result = et.xpath('//a/@href') # // 表示从任意位置开始找 -- 任意位置开始的 a标签的 href属性值
result = et.xpath('//span[@class="title"]/text()') # [@属性名xxx=属性值xx] 表示属性上的限定
# 满足包含多个属性的元素
result = et.xpath('//div[contains(@class,"pic") and contains(@id,"next")]/span/text()')
# 包含 @class="pic"并且 @id="next" 的 div节点
# 选择以 某个属性名=xxx 开头的元素
result = et.xpath('//li[starts-with(@name,"navIndex")]/ul/li/a')
# 成对出现(匹配到多个节点)
# 用循环实现
result = et.xpath('/html/body/div[@class="item"]/ul/li')
for item in result:
href = item.xpath('./a/@href')[0] # ./表示当前这个元素
text = item.xpath('./a/text()')[0]
print(href, text)
更多内容: https://blog.csdn.net/qq_50854790/article/details/123610184
2. 实战
html = requests.get(url)
# ② 加载数据,返回element对象
et = etree.HTML(html) # et是一个element对象
res1 = et.xpath('/html/body/div/span/text()') # text()表示提取标签中的文本信息
print(res1)
result = et.xpath('//div[@class="title"]/span/text()') # [@属性名xxx=属性值xx] 表示属性上的限定
print(res2)
res3 = et.xpath('//div[contains(@class,"pic") and contains(@id,"next")]/div/ul/li')
# 包含 @class="pic"并且 @id="next" 的 div节点
print(res3)
res4 = res3[0].xpath('./text()') # 拿到的节点还能继续使用 xpath 进一步获得节点
print(res4)
# 成对出现(匹配到多个节点)
# 用循环实现
result = et.xpath('/html/body/div[@class="item"]/ul/li')
for item in result:
href = item.xpath('./a/@href')[0] # ./表示当前这个元素
text = item.xpath('./a/text()')[0]
print(href, text)
二、多线程 和 线程池
1. 多线程
''' 多线程 让程序能同时执行多个任务 1.导包 from threading import Thread 2.创建任务 def func()... 3.创建线程 t = Thread(target=func,args=(xxx,xxx)) # target=目标函数 , args=(参数1,参数2...) 4.启动线程 t.start() ''' from threading 标签:
2431tj62连接器