基础知识

爬虫(spider),是指向网站/网络发起请求,获取、分析并提取有用信息的程序。

爬虫架构

Python 爬虫架构主要由五个部分组成,分别是调度器、URL管理器、网页下载器、网页解析器、应用程序。

  • 调度器:调度url下载器,网页下载器,网页解析器之间的工作。
  • url管理器:url队列管理,设置爬取顺序权重,防止重复爬取和循环爬取,将需要使用的url传递给网页下载器。
  • 网页下载器:自动获取网页内容,下载产出HTML文件传递给网页解析器。
  • 网页解析器:从HTML中提取数据并存储,提取新的url补充至url管理器。
  • 应用程序:由提取出的有用数据组成的应用。

http协议–请求与响应

request:用户将信息通过浏览器发送给服务器。

response:服务器接收并分析请求,返回数据。

爬虫需要一定的http基础,请自行搜索。

可参考Python爬虫详解(一看就懂)-CSDN博客

vscode环境搭建

下载扩展pip manager:便于pip安装管理扩展。下文的扩展请自行搜索下载。



网页下载器requests

request

request请求

response

response响应

简单操作

1
2
3
4
5
6
7
8
import requests

url = 'https://luvmand.top/'
r = requests.get(url)

print(r.status_code) #200
print(r.headers) # 报文
print(r.encoding) # utf-8


URL管理器

通过设置url管理器实现urls的管理和存储

url管理器的基本代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class url_manager:
# 初始化
def __init__(self):
self.new_urls = set() #初始化待使用urls集合
self.old_urls = set() #初始化已使用urls集合

# 添加url
def add_new_url(self, urls):
# 判断urls是否为空
if urls is None or len(urls) == 0:
return

#循环添加所有url
for url in urls:
# 判断是否已存在
if url in self.new_urls or url in self.old_urls:
continue
self.new_urls.add(url)

# 获得url
def get_url(self):
if self.if_has_new_url():
# pop移除集合中的元素
url = self.new_urls.pop()
self.old_urls.add(url)
return url

# 判断未使用url是否为空
def if_has_new_url(self):
return len(self.new_urls) > 0



网页解析器Beautiful Soup

通过第三方库beautiful soup从html中提取数据

beautiful soup讲解:Python爬虫:史上最详细的BeautifulSoup教程 - 简书 (jianshu.com)

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
from bs4 import BeautifulSoup

url = 'https://luvmand.top/'
r = requests.get(url)

# 通过text操作获取网页的html文件
text = r.text
# beautiful soup读取html文件
soup = BeautifulSoup(text,'html.parser')
# 查找内容
content = soup.find_all('div',class_='content')
for t in content:
if t.string is not None:
print(t.string,'\n','/'*30,'\n')

可将本人博客首页的内容打印出。



内容查找:正则表达式与xpath

正则表达式匹配内容

正则表达式入门:正则表达式 – 语法 | 菜鸟教程 (runoob.com)

通过正则表达式可以更方便地匹配内容

python通过import re来使用正则表达式.

1
2
3
4
5
6
7
8
9
import re
urls = soup.find_all('a')
#一个与博客地址格式相同的正则表达式
pattern = r'^/\d+/\d+/\d+/[\s\S]+$'
for url in urls:
# 匹配与正则表达式相同的超链接
web = re.match(pattern,url['href'])
if web is not None and url.string is not None:
print(url.string,'https://luvmand.top'+web.group())

xpath

xpath可以更加便捷地提取源码中的信息。

xpath教学:Python爬虫实战之xpath解析_python xpath-CSDN博客



网页自动化Selenium

很多网站重要数据采用动态加载,单凭Beautiful Soup无法得到数据。通过selenium可直接模拟浏览器对象,便于查找动态元素

使用前需要配置虚拟浏览器环境

查阅selenium教学:selenium入门超详细教程——网页自动化操作-CSDN博客

通过selenium的元素查找主要依赖webdriver.find_element():

webdriver查找

简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
from selenium import webdriver
# 若出现NameError: name 'By' is not defined,请在头部添加引入:
from selenium.webdriver.common.by import By

# 打开浏览器,此处使用Chrome
browser = webdriver.Chrome()
# 访问我的博客
browser.get(r'https://luvmand.top/')
# 查找打印首页博客内容
elements = browser.find_elements(By.CLASS_NAME,'content')
for element in elements:
print(element.text,'\n')
browser.close()

通过get_attribute()可以得到元素的信息,如abc_url = browser.find_element(By.ID,'abc').get_attribute('src')



实例:爬取动漫网站视频

在此例中,动漫网站的视频链接为动态元素,且依赖DPlayer加载ts视频,不能直接通过m3u8链接进行下载,需要通过第一个m3u8链接得到第二个链接。

DPlayer

选中视频元素,得到以下代码:

动漫网站目标

代码中的src可以通过浏览器直接访问得到可观看的视频,但无法进行下载;

裁剪&url=后的代码可以得到ffmpy3下载链接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import re
import requests
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
# parse可用于转义url中的百分号,如:https%3A%2F%2Fvip.ffzy-play6.com%2F20221213%2F18032_4edec70b%2Findex.m3u8
from urllib import parse
# 前面提到的URL Manager
import manager
# ffmpy3可用于ts视频的下载与格式转换,使用前需要先配置环境
import ffmpy3

# 网站根地址
root_url = 'https://iyhdmw.com/'
# 输入需要下载的动漫主页面
list_number = input('输入番剧在网址上的代码(例如https://www.iyhdmw.com/showp/12089.html 为12089)\n')
video_list_menu_url = root_url+'showp/'+list_number+'.html'
if len(list_number) == 0:
print('未输入代码,程序结束')
quit()
print('爬取番剧地址为'+video_list_menu_url)
#访问动漫主页面
response = requests.get(video_list_menu_url)
if response.status_code != 200:
raise print('动漫主页面访问失败')
quit()
else:
print('访问成功')

#beauifulSoup爬取各集网址
index_html = response.text
soup = BeautifulSoup(index_html,'html.parser')
# 打印标题
print(soup.title.text)
# 统计集数
player_choose = soup.findAll('div',class_='movurl')[1]
episode_list = player_choose.findAll('li')
# print(episode_list)
episode_number = len(episode_list)
print(f'共{episode_number}集')
# 将每集地址存入url管理器
manager = manager.url_manager()
for episode in episode_list:
#提取超链接并拼接
episode_url = episode.find('a')
manager.add_new_url({root_url+episode_url.get('href')})

# 存档视频超链接
list_urls = open(f'./file/sakura/list_urls_{soup.title.text}.txt','w',encoding='utf-8')
# 对每集进行爬取
while (manager.if_has_new_url()):
url = manager.get_url()
# 通过selenium爬取动态数据iframe内容
# 使用webdriver模拟爬取
browser = webdriver.Chrome()
# 以下代码可以防止被识别为爬虫
browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
# 加载网页
browser.get(url)
# 保证加载时间,此处为隐式加载,时间最大不超过10s
browser.implicitly_wait(10)

# 查找视频源地址
video_browser_url = browser.find_element(By.ID,'yh_playfram').get_attribute('src')
# 存档浏览器观看地址
list_urls.write(f'{browser.title}: 浏览器观看: '+video_browser_url)
# 切割url
video_url_cutted = re.split(r'&url=',video_browser_url)
# 转义url中的百分号
video_currect = parse.unquote(video_url_cutted[1])
print(video_currect)
# 存档下载视频地址(提取出超链接即成功)
list_urls.write(' ffmpeg下载连接: '+video_currect+'\n')
# # 下载视频
# ff = ffmpy3.FFmpeg(inputs={video_currect:None},outputs={f'./file/sakura/{soup.title.text}/{browser.title.text}.mp4':None}).run()

list_urls.close()

程序执行完成后,可成功得到含m3u8链接的txt文件:

动漫网站成功示例

防止被识别为爬虫代码参考自:selenium webdriver 开启网页失败,被发现为爬虫,的解决办法_driver.execute_cdp_cmd(“page.addscripttoevaluateon-CSDN博客