基于python爬虫————静态页面和动态页面爬取
基于python爬虫————静态页面和动态页面爬取
文章目录
- 基于python爬虫————静态页面和动态页面爬取
- 1. 爬虫的原理
- 2. 用正则表达式匹配获取数据
- 3. 页面源码解析
- 4. 通过requests请求,爬取静态页面
- 4.1 **css选择器基本语法**
- 4.2 **xpath基本语法**
- 4.3 通过代理进行数据的采集
- 5. 通过selenium驱动浏览器,爬取动态页面
- 6. 利用多线程提升爬取效率
- 简单总结
- 语录
1. 爬虫的原理
我们在浏览器里面搜索需要查找的目标,这时浏览器会与服务器之间建立一个通常被称作为会话。我给你一个请求你给我做出一个响应。这样就可以得到我们想要获取的知识。
在爬虫中要进行一个伪装,把爬虫伪装一个游览器,防止对ip进行封闭。通常解决办法带上请求头内容,让服务器知道自己是一个浏览器进行访问。
爬虫就是根据上面的这一原理,通过url(资源定位符)向服务器发送请求,随后服务器会做出响应。不过这时我们得到的是一个网页源代码。
百度首页
百度首页源码
爬取打印显示
通过上面的比较我们爬取得到的也就是网页的源代码,我们只需要对页面返回内容做一个筛选,提取我们想要的那部分数据即可,但通常许多网站都有反爬虫机制需要进行预处理。最后把获取到的数据写入到文件或数据库中进行数据的持久化保存。
步骤:通过url发送请求 ————>等待服务器做出响应——————>得到网页源码————>解析页面————>进行筛选————>数据保存
在上述步骤中得到网页源代码和解析页面最为重要,这两部分通常也是最难的,其中主要面对网页上的各种反爬机制,有时候你的道德页面源代码未必就是和网页源代码一模一样的,需进行一个对比分析。
2. 用正则表达式匹配获取数据
正则表达式对于爬虫比起其它方法去获取数据来讲是一个非常高效的手段,它只需要通过返回的网页源码字符串,通过在某种特殊的模式下进行模式匹配,就可以找到我们想要的那部分数据。但正则表达式的匹配模式写起来也是有点难度对于初学者来讲做到快速精确匹配。
使用正则表达式可以归纳为以下两种:
-
在自己写好的模式进行编译,创建模式对象(pattern) ,再通过掉函数并且函数里面只需要传入要匹配的字符串。
-
通过
re.函数名('匹配模式','需要匹配的字符串')
来找到需要的字符
当同一个匹配模式需要用到多次选择第一种方法,当匹配模式只需要用到一次选择第二种方法。
正则表达式匹配成功返回的数据类型为字符串,匹配不成功返回None。但我们查看匹配结果时最好进行一个判断是否匹配成功,防止报错。
常用的匹配函数如下:
函数名 | 对应功能说明 |
---|---|
match() | 从前往后进行匹配,只匹配一次。匹配成功返回re.Match 对象再通过调用group()查看对应的值 |
fullmatch() | 从前往后进行匹配,只匹配一次并且进行完整匹配 |
search() | 搜索满足匹配模式的字符串,只匹配一次 |
findall() | 搜索满足所有满足匹配模式的字符,返回一个列表,列表里面的每一个元素就是满足匹配模式的字符串 |
在爬虫里面通常使用findall进行匹配找到所有满足匹配模式的字符串,通过遍历列表查看数据
正则表达式的特殊字符含义如下:
数量限定符
特殊字符
\u4e00-\u9fa5 | 只匹配中文字体 |
---|
位置相关字符
常用匹配字符
3. 页面源码解析
页面展示出来的内容
页面源码
通过两张图片的对比我们可以看出我们看到的和服务器返回给我们的还是有很大的区别的。我们在页面上既可以看见图片和文字还可以看见一些视频,但网页源码里面全都是纯文本,图片和视频在源码中对应的是一个个链接。那就意味着我们获取图片和视频等还需要源代码提供的链接进行访问。
在爬虫里面对网页源码进行分析是一项非常重要的技能,需要通过不断地探索找出一些共性。最好对css和JS有一定的了解,了解网页展示给我所看到的原理。
4. 通过requests请求,爬取静态页面
对于通过requests请求页面得到源代码需要进行一个预处理,常用的预处理通过css选择器和xpath进行筛选出我们想要的内容。
4.1 css选择器基本语法
选择器 | 示例 | 示例说明 |
---|---|---|
.class | .intro | 选择所有的class="intro"的元素 |
#id | #firstname | 选择所有id="firstname"的元素 |
* | * | 选择所有元素 |
elemet | p | 选择所有 元素 |
element,element | div,p | 选择所有
元素和
元素 |
element element | div p | 选择
元素内的所有
元素 |
在用css选择器定位元素时,当有可以通过id进行定位首选id,id属性对应的值是唯一的,然后再考虑class属性进行地位元素,如果class属性中有多个值值需要选择一个即可。
4.2 xpath基本语法
官方帮助文档:
表达式 | 说明 |
---|---|
nodename | 选取此结点的所有子节点 |
/ | 从当前节点选取子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
xpath还可以结合运算符对元素进行定位
xpath中的谓语(Predicates)
谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中。
选取未知节点
选择若干路径
有了上面的基础下面通过案例进行具体分析:
-
通css选择器方法获取豆瓣前25的电影名
import requests from bs4 import BeautifulSoup as Bs url = 'https://movie.douban.com/top250' # 设置请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36', # 先登录,通过开发者工具控制台获取 'Cookies': '**************************************************' } # 建立连接 resp = requests.get(url=url, headers=headers) print(resp.status_code) # 查看是否访问成功,出现200就说明访问成功 if resp.status_code == 200: soup = Bs(resp.text, 'lxml') # 进行预处理 title_list = soup.select('div.hd > a > span:nth-child(1)') # 找到标题对应的标签 for title in title_list: print(title.text, end='\t') # 打印输出电影标题 ''' 200 肖申克的救赎 霸王别姬 阿甘正传 这个杀手不太冷 泰坦尼克号 美丽人生 千与千寻 辛德勒的名单 盗梦空间 忠犬八公的故事 星际穿越 楚门的世界 海上钢琴师 三傻大闹宝莱坞 机器人总动员 放牛班的春天 无间道 疯狂动物城 大话西游之大圣娶亲 熔炉 教父 当幸福来敲门 龙猫 怦然心动 控方证人 '''
-
通xpath选择器方法获取豆瓣前25的电影名
import requests
from bs4 import BeautifulSoup as Bs
from lxml import etree
url = 'https://movie.douban.com/top250'
# 设置请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36',
'Cookies': '**************************************'
}
# 建立连接
resp = requests.get(url=url, headers=headers)
if resp.status_code == 200:
html = etree.HTML(resp.text) # 预处理
title_list = html.xpath('//div[@class= "hd"]/a[@href]/span[1]/text()')
for title in title_list:
print(title, end='\t')
'''
肖申克的救赎 霸王别姬 阿甘正传 这个杀手不太冷 泰坦尼克号 美丽人生 千与千寻 辛德勒的名单 盗梦空间 忠犬八公的故事 星际穿越 楚门的世界 海上钢琴师 三傻大闹宝莱坞 机器人总动员 放牛班的春天 无间道 疯狂动物城 大话西游之大圣娶亲 熔炉 教父 当幸福来敲门 龙猫 怦然心动 控方证人
'''
在爬取页面数据我们通常选择xpath进行爬取上明显比通过css选择器取定位元素更加灵活。当进行静态页面的爬取都是通过上面的步骤来进行数据的抓取,只是有些要进行登录才能够获取它的网页内容随机应变即可。
4.3 通过代理进行数据的采集
当我们对同一个网页进行访问过于平凡,有些网站会进行反爬机制对你的ip进行封闭。这就要求我们必须要不同的ip地址对同一个网站进行多次访问。这时只能通过购买ip让别人替你转发在把结果返回给你。
通过运营商提供的ip链接获取有效ip
# -*- coding: UTF-8 -*-
"""
此代码为代理IP可用性检测模块,可准确筛选出尚未失效IP
注:
1.此代码只针对TXT数据格式接口。
2.尚未失效IP不一定为爬虫可用IP
3.使用时,请调用check_ip(url),url为TXT数据格式接口地址
"""
import requests
import telnetlib
import re
from concurrent.futures.thread import ThreadPoolExecutor
# 请求接口,匹配出代理IP,多线程检测
def check_ip(url):
real_ip = []
# 检测代理IP是否失效
def telnet_ip(ip, port):
try:
telnetlib.Telnet(ip, port, timeout=1)
real_ip.append(f'{ip}:{port}')
except:
pass
while True:
try:
resp = requests.get(url)
# print(resp.text)
ip_data = re.findall('(\d+\.\d+\.\d+\.\d+):(\d+)', resp.text)
with ThreadPoolExecutor(max_workers=16) as pool:
for ip, port in ip_data:
pool.submit(telnet_ip, ip, port)
return real_ip
except:
pass
调用ip进行爬取
"""
example04-利用代理爬取
Version:
Author:
Date:2021/8/17
"""
from check_proxies import check_ip
import requests
from bs4 import BeautifulSoup
# ip地址链接,由运营商提供,具有一定的时效性
URL = 'http://api.66daili.cn/API/GetCommonProxy/?orderid=2291244402101903832&num=20&token=66daili&format=text&line_separator=win&protocol=http&anonymous=elite,anonymous,transparent&area=%E4%B8%AD%E5%9B%BD&proxytype=https&speed=fast#api'
ip_list = check_ip(URL)
print(ip_list)
flag = True
while flag:
for i in range(len(ip_list)):
doubna_url = 'https://movie.douban.com/top250'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
}
# 代理ip,会根据要求进行自动选择http或https协议
proxy = {
'http': 'http://' + ip_list[i],
'http': 'https://' + ip_list[i]
}
try:
resp = requests.get(url=doubna_url, headers=headers, proxies=proxy, timeout=1)
if resp.status_code == 200:
print(resp.text)
flag = False
break
except:
print('Erro')
5. 通过selenium驱动浏览器,爬取动态页面
- 对于有些页面是一个动态加载,当在通过requests方法取获取那肯定是不可取的。这是就需要一个动态抓取页面的三方库来支持不断地更新页面加载的抓取。
- 利用selenium动态获取页面数据效率上比较低
- 需要用到对应浏览器的驱动 谷歌浏览器的驱动下载
通过selenium获取页面步骤:
先把驱动放入当前项目下
from selenium import webdriver
url = 'https://www.baidu.com/'
# 创建浏览器对象
b = webdriver.Chrome(./chromedrivers.exe)
# 访问页面
b.get(url)
# 关闭
b.quit()
使用selenium登录京东页面
import getpass
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
url = 'https://www.jd.com/'
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ['enable-automation', 'enable-logging']) # 忽略警告
# options.add_argument('blink-settings=imagesEnabled=false') # 不加载图片
b = webdriver.Chrome(options=options)
b.get(url)
b.find_element_by_id('ttbar-login').click()
b.find_element_by_class_name('login-tab-r').click()
b.find_element_by_id('loginname').send_keys(input('输入账号:'))
b.find_element_by_id('nloginpwd').send_keys(getpass.getpass('输入密码:'))
b.find_element_by_id('loginsubmit').click()
time.sleep(5)
b.find_element_by_id('key').send_keys('python')
b.find_element_by_id('key').send_keys(Keys.ENTER)
max_y = 10000
y = 0
while y <= max_y:
b.execute_script(f'window.scrollTo(0,{y})')
time.sleep(1)
y += 1000
selenium里面同时支持css选择器和xpath定位元素的方法,不需要再导入。只需要通过创建的浏览器对象调用相应的函数即可。
使用selenium实现自动登录注意事项:
- 对于有些登录页面嵌套在iframe框里需要进行跳转,最后登录成功后在跳转到默认页面
- 最好设置隐式等待或显示等待
6. 利用多线程提升爬取效率
- 对于网络数据的采集是非常耗费计算机资源的。当任务比较多用一个单线程进行来爬程序就会显得很慢,用多个线程来同时完成这些任务就会很大程度上提高效率了。
- 程序的执行速度主要决定因素还是靠计算机的性能和网速的影响
使用多线程下载图图片
"""
example02-
Version:
Author:
Date:2021/8/21
"""
import time
import requests
import json
from concurrent.futures import ThreadPoolExecutor
def download_img(href, num):
"""下载图片"""
resp = requests.get(url=href)
with open(f'./img/{num}.jpg', 'wb') as file:
file.write(resp.content)
def main():
"""程序执行入口"""
count = 0 # 文件命名
for i in range(0, 90, 30):
url = f'https://image.so.com/zjl?ch=wallpaper&sn={i}'
resp = requests.get(url)
# resp.json()
data = json.loads(resp.text)
# 单线程 下载
# for j in data['list']:
# """找到图片地址"""
# count += 1
# print(j['imgurl'])
# download_img(j['imgurl'], count)
# 多线程
with ThreadPoolExecutor(max_workers=16) as pool:
for j in data['list']:
"""找到图片地址"""
count += 1
pool.submit(download_img, j['imgurl'], count) # 第一个参数传入函数名,后面参数传入对应的函数的参数
if __name__ == '__main__':
start = time.time()
main()
end = time.time()
print(f'用时:{end - start}')
- 当遇到任务量比较大,任务与任务之间相互独立的情况下可以考虑使用多线程来提交任务。
简单总结
通过网络数据的采集课程,基本掌握了爬取数据的一些手段。当对于不太常规的网页源代码不太容易获取相应的对策。总的来讲爬虫还是很有趣的能够把前面学到的python基础知识综合的运用起来,还能提高对细节的洞察能力。
语录
只要爬虫学得好,坐牢少不了。路漫漫其修远兮,吾将上下而求索!
龍龖龘讋少: 其实用一些数据分析的方法也可以生成可能性最大的号码,比如说在三次彩票中都没有中奖,可以聚类来判断三次的分布,然后就根据线性回归可以预测一下下一次的号码了
Zht_bs: 不错
宇宙爆肝锦标赛冠军: 受教了!
小超0748: 大佬 666
python xxxyyyxxxyyy: 不错,继续加油!希望后面能有更多分享!期待你的回访和关注,我们一起交流学习呀