介绍
爬虫属于灰色地带的,本教程仅用于编程学习的展示,所引发的所有问题与本人、本站无关,请仅用于正经学习
Python爬虫的基本教程,本教程主要使用 requests
库来获取网页的原数据,使用 BeautifulSoup
解析网页
环境和依赖
请自行安装并配置好Python的环境,并导入所需要的以来项目。你可以使用pip
导入或者使用Anaconda
手动管理所需要的包,或者直接使用以下requirements.txt
自动导入所需。
在目录创建 requirements.txt
文件,填入以下内容
requests==2.24.0
beautifulsoup4==4.9.3
lxml==4.5.2
保存文件后 Shift+鼠标右键
点击 在此处打开PS窗口
(CMD也行吧)执行以下命令
pip install -r requirements.txt
当然你可以手动安装这些包,已经存在这些包就不用安装了
开始使用
准备好所有的环境后可以开始爬取数据了,直接贴代码:
内容的爬取
导入所需要的依赖
import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse
import time
请求头部信息,如图:
每个站点所需要的内容不一样,不同浏览器也不一定相同,按需携带请求头。你还可以携带站点所需要的Cookie等信息来模拟更多,比如:已登录用户
def Request(url):
try:
#模拟头部信息,不同浏览器不一定相同,从F12-Network自行查看,自行填写爬取所需要发生的头部信息
#某些站点有反爬虫,这就需要你尽可能的把爬虫伪装成“一个真实的用户”
headers = {}
headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4260.0 Safari/537.36 Edg/87.0.637.0'
headers['Accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9'
headers['Accept-Language'] = 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6'
headers['Host'] = urlparse(url).hostname
headers['Connection'] = 'keep-alive'
headers['Accept-Encoding'] = 'gzip, deflate, br'
headers['Cache-Control'] = 'max-age=0'
html_raw_str = requests.get(url, headers=headers) #发送的是GET请求 POST请看下面那块
html_raw_str.encoding = 'utf-8'
if html_raw_str.status_code == 200:
return html_raw_str.text
else:
return False
except BaseException as r:
with open('Run.log', 'a', encoding='utf-8') as file_object:
file_object.write("在 {} 请求URL时出现异常:{}".format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), str(r) + "\n\r"))
return False
常见的请求方式是GET和POST,更多请访问:该链接或者自行百度,限于篇幅不具体展开
#POST块
......
data = {'data1': data1,'data2': data2,'data3': data3...}
html_raw_str = requests.post(url, headers=headers, data=data)
html_raw_str.encoding = 'utf-8'
......
例子:
txt=Request('https://aiuu.top/')
print(txt)# <!DOCTYPE html><html><head><title>哎唷导航</title>......
#输出一堆很长的内容,这个就是网页的内容,如果你将这些内容保存为 1.html 打开就是一个显示不全但是内容都有的“哎唷导航”的界面
通过上述内容我们获取到了原始的网页内容,在没有反爬虫的网页爬取内容其实并不困难,难度和在记事本中打开链接是差不多的。
内容的提取
内容的提取涉及的内容比较多,至少需要知道 HTML 的基本结构、还有 CSS选择器 或者JS(JQ)的选择器的知识;说白了就是需要有一点前端的知识。限于篇幅只是讲个皮毛。老师一学期都讲得不清不楚,就别指望我讲得清清楚楚了。
一次性访问得太急太快,大多数网站都会把你加入“黑名单”的,添加:
time.sleep(5) #运行到这里停住5秒
以上代码可以进行自我“减速”
以下以 豆瓣电影 - 哆啦A梦:大雄的新恐龙 映画ドラえもん のび太の新恐竜 (2020) 为例子
浏览器打开页面可以正常看到网页的内容还有多打开几个页面看看内容是不是有规律性,豆瓣电影的项目都是像上图这样的(不改版的话)
到这里需要确定自己要这个页面的什么内容,比如我们要提取 电影名字、年份、简介、IMDB链接、评分、几人评分、哪里看
等等内容,
一般查看页面源代码
能看到(搜索到)的内容都可以直接获取到,先确定自己要获取的内容是否直接包含在这里面,如果没有直接包含在这,那就可能是通过其他方法多次加载进来的,需要更进一步处理
如果需要的内容直接在源代码中可以看到,则可以F12
或右键-检查(元素)
左上角的“鼠标图标”可以帮助快速定位元素
比如这边定位标题,标题在一个ID为content的元素下的h1标签下的第一个span标签里,这样说比较拗口,在选择器中表示#content > h1 > span:nth-child(1)
如果不明白选择器,在提取内容上会是比较麻烦的,相关的文档可以去看看
以上方法可以快速复制选择器,不过这种方法比较局限,一般只针对一模一样的页面
比如复制“哆啦A梦:大雄的新恐龙”的IMDb链接为 #info > a:nth-child(29)
而复制“信条”的IMDb链接为 #info > a:nth-child(30)
是不一样的,如果在程序中固定为 #info > a:nth-child(29)
那么在提取其他电影时则会出错或者获取到不正确的内容,正确的选择器为 #info > a:last-of-type
(方法不唯一)
是的很烦,是需要选择器相关的知识的,方法相对灵活,像这个也可以 #info span.pl:last-of-type + a
使用临近选择器...总之需要选择器的知识...
以下开始内容的提取,代码:
raw_page = Request('https://movie.douban.com/subject/34454004/') # 获取这个页面的原始内容
if raw_page:
# bs = BeautifulSoup(raw_page,"html.parser") #执行速度适中
# bs = BeautifulSoup(raw_page, "html5lib") # 最好的容错性 速度慢
bs = BeautifulSoup(raw_page, "lxml") # 如果仅需要CSS选择器的功能 速度快
content_block = bs.select_one("#content") # 获取id为content这个块
title = content_block.select_one("h1 span:nth-child(1)").text # 在content_block里面选择<h1>标签下的第一个<span>的text
# 或者你可以直接在所需要的节点右键复制,如图
title2 = bs.select_one("#content > h1 > span:nth-child(1)").text
print(title) # 哆啦A梦:大雄的新恐龙 映画ドラえもん のび太の新恐竜
print(title2) # 哆啦A梦:大雄的新恐龙 映画ドラえもん のび太の新恐竜
# 以上两种方法都可以获取到标题,这取决于目标网页的结构
# 获取年份
year = content_block.select_one("h1 span.year").text # 在content_block里面选择<h1>标签下有个class为year的<span>的text
#获取IMDb链接的链接呢?
imdb_link = content_block.select_one("#info a:nth-child(29)").text #在content_block里面选择id为info下的第29个<a>标签的text
print(imdb_link)
#你会发现这里有很严重的问题,第29个?也许爬其他电影时这个可能在第28或者30呢。这里只能你多打开几个电影去找规律了,或者干过直接搜索“IMDb链接:”后的<a>标签
#我这里是发现IMDb链接如果有的话会是id为info下的最后一个
imdb_link = content_block.select_one("#info > a:last-of-type").text #在content_block里面选择id为info下的最后一个<a>标签的text
print(imdb_link)
# 获取到的内容是tt10588750,明显不是我们要的https://www.imdb.com/title/tt10588750,再改正
imdb_link = content_block.select_one("#info > a:last-of-type").attrs['href']
print(imdb_link)
#获取其他只有一个元素的都是同理
#获取热评列表
comments=content_block.select("#hot-comments .comment-item")#select和select_one不同,select_one选一个,select选多个
for i in comments:
print(i.select_one('.comment-info a').text)#获取评论者昵称
print(i.select_one('.comment-info span.rating').attrs['title'])#获取评论者打分
print(i.select_one('.comment-content .short').text)#获取该评论者的评论
一些注意事项和使用方法都写在了注释里面了,就不多说了。反正重点就是在选择器上面,举一反三就可以获取想要的内容了。
以上都是针对爬取网页的,不同网站有不同的数据返回格式,比如:返回的是JSON、XML、JS等,例子如下
如果直接访问:https://tp.aiuu.top/Core.php?Action=Init 返回的是JSON的字符串内容:
{buff: 0, text: "请重新验证!", identify: 0}
像这种内容是很方便的,可以直接序列化后使用,具体可访问:Python JSON ,以下是简单的使用实例:
import json
raw = Request('https://tp.aiuu.top/Core.php?Action=Init')
obj = json.loads(raw) # {buff: 0, text: "请重新验证!", identify: 0}
print(obj['text']) # 请重新验证!
数据的存储
爬完内容后我们存储内容也可以使用JSON
#比如上面的豆瓣例子,我们存储评论
# 获取热评列表
......
comments = content_block.select("#hot-comments .comment-item") # select和select_one不同,select_one选一个,select选多个
comment_list = {} # 评论列表
for i in comments:
print(i.select_one('.comment-info a').text) # 获取评论者昵称
print(i.select_one('.comment-info span.rating').attrs['title']) # 获取评论者打分
print(i.select_one('.comment-content .short').text) # 获取该评论者的评论
comment_list[i.select_one('.comment-info a').text] = {'content': i.select_one('.comment-content .short').text, 'rating': i.select_one('.comment-info span.rating').attrs['title']}
with open('comment_list.txt', 'a', encoding='utf-8') as file_object:
file_object.write(json.dumps(comment_list, ensure_ascii=False))
这样同目录下就会生成一个 comment_list.txt
的文件,里面便是JSON字符串,下次使用可以读取后使用 json.loads
解码就可以使用了。
其实GayHub上有一些开源的爬虫,也可以去看看,说不定有用。
本文由 Ainto 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: 2021-05-20 11:26
大神!