python爬虫(基础)
1.任务介绍
爬取豆瓣电影TOP250的基本信息,包括电影的名称,豆瓣评分,评价数,电影概况,电影链接
https://movie.douban.com/top250
2.爬虫介绍
-
什么是爬虫
网络爬虫,是一种按照一定规则,自动抓取互联网信息的程序或者脚本。由于互联网数据的多样性和资源的有限性,根据用户需求定向抓取相关网页并分析已成为如今主流的爬取策略
-
爬虫可以做什么
只要你能够通过浏览器访问的数据都可以通过爬虫来获取
-
爬虫的本质是什么
模拟浏览器打开网页,获取网页中我们想要的那部分数据
3.基本流程
-
准备工作
通过浏览器查看分析目标网页,学习编程基础规范
-
获取数据
通过HTTP库向目标站点发起请求,请求可以包含额外的header等信息,如果服务器能正常响应,会得到一个Response,便是所要获取的页面信息
-
解析内容
得到的内容可能是html,json等格式,可以用页面解析库,正则表达式等进行解析
-
保存数据
保存形式多样,可以存为文本,也可以保存到数据库,或者保存特定格式的文件
3.1准备工作
页面包含250条电影数据,分10页,每页分25条
每页的URL的不同之处:最后的数据 = (页数-1)*25
3.1.1分析页面
借助F12来分析网页,在Elements下找到需要的数据位置
3.1.2编码规范
-
一般Python程序第一行需要加入
--coding:utf-8 -- 或者# coding = utf-8
这样可以在代码中包含中文
-
在python中,使用函数实现单一功能或相关联功能的代码段,可以提高可读性和代码重复利用率,函数代码块以def关键词开头,后接空格,函数标识符,名称,圆括号(),冒号:,括号中可以传入参数,函数段缩进,return用于结束函数,可以返回一个值,也可以不带任何表达式(表示返回None)
-
python文件中可以加入main函数用于测试程序
if __name__ =="__main__"
-
python使用#来添加注释,说明代码(段)的作用
3.1.3引入模块
- 模块(module):用来从逻辑上组织python代码(变量,函数,类),本质就是py文件,提高代码的可维护性。python使用import来导入模块
3.2获取数据
- python一般使用urllib库获取页面
#得到指定url的网页内容
def askURL(url) :
#用户代理表示告诉豆瓣服务器我们是什么类型的机器,浏览器(本质上是告诉浏览器,我们可以接收什么水平的数据)
head = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0"}
request = urllib.request.Request(url,headers=head)
html = ""
try :
response = urllib.request.urlopen(request)
html = response.read().decode("utf-8")
print(html)
except urllib.error.URLError as e :
# hasattr()用于判断对象是否包含某一属性
if hasattr(e,"code") :
print(e.code)
if hasattr(e,"reason") :
print(e.reason)
3.3解析数据
3.3.1标签解析
-
Beautiful Soup
Beautiful Soup是一个库,提供一些简单的,python式的用来处理,导航,搜索,修改分析书等功能,通过解析文档为用户提供需要抓取的数据。我们需要的每个电影都在一个==
的标签中,且每个div 标签都有一个属性class = “item”==。
soup = BeautifulSoup(html,"html.parser") #创建beautifulsoup对象
for item in soup.find_all("div",class_="item") : # 查找符合要求的字符串形成列表
3.3.2正则提取
-
正则表达式
正则表达式,通常被用来检索,替换那些符合某个模式(规则)的文本。正则表达式是对字符串操作的一种逻辑公式,就是用事先定义号的一些特定字符及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。python中使用re模块操作正则表达式。
#影片详细链接的规则
findlink = re.compile(r'<a href="(.*?)">') # 创建正则表达式对象,表示规则
#影片图片链接
findimgsrc = re.compile(r'<img.*src="(.*?)"',re.S) # re.S让换行符包含在字符中
#影片片名
findtitle = re.compile(r'<span class="title">(.*)</span>')
#影片评分
findrating = re.compile(r'<span class="rating_num" property="v:average">(.*)</span>')
#评价人数
findjudge = re.compile(r'<span>(\d*)人评价</span>')
#找到概况
findinq = re.compile(r'<span class="inq">(.*)</span>')
#找到影片相关内容
findbd = re.compile(r'<p class="">(.*?)</p>',re.S)
3.4保存数据
3.4.1 Excel表储存
-
excel表格存储
利用python库xlwt将抽取的数据datalist写入excel表格
基础应用:
import xlwt
workbook = xlwt.Workbook(encoding="utf-8") # 创建workbook对象
worksheet = workbook.add_sheet("sheet1") # 创建工作表
worksheet.write(0,0,"hello") # 写入数据,第一行参数表示行,第二个参数表示列,第三个参数表示内容
workbook.save("student.xls") # 保存数据
补充urllib
get和post请求
import urllib.request
#获取一个get请求
response = urllib.request.urlopen("http://www.baidu.com")
print(response.read().decode('utf-8')) #对获取到的网页源码进行utf-8解码
#获取一个post请求
#一般用于模拟用户登录
import urllib.parse #引入解析器
data = bytes(urllib.parse.urlencode({"hello":"world"}),encoding = 'utf-8')#转变二进制 传入 账号:密码或者cookie信息
response = urllib.request.urlopen("https://httpbin.org/post",data=data)
print(response.read().decode('utf-8'))
模拟浏览器和真实浏览器返回信息相同
#get请求
import urllib.request
response = urllib.request.urlopen("https://httpbin.org/get")
print(response.read().decode('utf-8'))
模拟浏览器和真实浏览器user-agent不相同
超时处理
import urllib.request
#超时处理
try:
response = urllib.request.urlopen("https://httpbin.org/get",timeout=0.01) #0.01秒
print(response.read().decode('utf-8'))
except urllib.error.URLError as e:
print("time out!")
#输出:time out!
获取响应标头
import urllib.request
response = urllib.request.urlopen("https://baidu.com")
#获取响应标头
print(response.getheaders())
爬虫处理
访问豆瓣时会报错418,这是由于豆瓣网站识别出了我们爬虫的身份
可以封装头部信息进行访问
url = "https://douban.com"
#封装信息
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0"}
req = urllib.request.Request(url = url,headers=headers)#构建请求对象
response = urllib.request.urlopen(req)
print(response.read().decode('utf-8'))
补充Beautifulsoup
Beautifulsoup4将复杂的HTML文档转换成一个复杂的树形结构,每个节点都是python对象,所有对象可以归纳为4种:
Tag,NavigableString,Beautifulsoup,Comment
Tag标签及其内容
from bs4 import BeautifulSoup
file = open("./baidu.html","rb")
html = file.read()
bs = BeautifulSoup(html,"html.parser") # 用html解析器进行解析
print(bs.a)
print(bs.title)
#输出为
#<a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!--新闻--></a>
#<title>百度一下,你就知道 </title>
print(type(bs.a))
#输出:<class 'bs4.element.Tag'>
但只能拿到它所找到的第一个内容
NavigableString标签里的内容
print(bs.title.string)
#输出为:百度一下,你就知道
print(type(bs.title.string))
#输出为:<class 'bs4.element.NavigableString'>
可以理解为字符串
属性:
print(bs.a.attrs)
#输出为:{'class': ['mnav'], 'href': 'http://news.baidu.com', 'name': 'tj_trnews'}
可以通过这种方式快速的拿到对应标签的属性
BeautifulSoup
表示整个文档
print(type(bs))
#输出:<class 'bs4.BeautifulSoup'>
print(bs.name)
#输出:[document]
Comment注释
是一个特殊的NavigableString
输出的内容不包含注释符号
print(bs.a.string)
#输出:新闻
print(type(ba.a.string))
#输出:<class 'bs4.element.Comment'>
文档的遍历
#tag的.content属性可以将tag的子节点以列表的方式输出
print(bs.head.contents)
#用列表索引来获取它的某一个元素
print(bs.head.contents[1])
更多内容:文档搜索
文档搜索
find_all
字符串过滤:会查找与字符串完全匹配的内容
t_list = bs.fing_all("a")
print(t_list)
search()
正则表达式搜索:使用search()方法来匹配内容
import re
t_list = bs.find_all(re.compile("a"))
#按照标签输出包含a的内容
方法
传入一个函数,根据函数的要求进行搜索
def name_is_exists(tag) :
return tag.has_attr("name")
#输入一个tag,来获取属性中对应的值
t_list = bs.find_all(name_is_exists)
print(t_list)
kwargs
传入参数(特征)
t_list = bs.find_all(id = "head")
for item in t_list :
print(item)
输出id = "head"的所有子内容
text
文本参数
t_list = bs.find_all(text = ["hao123","地图","贴吧"])
for item in t_list :
print(item)
#输出:hao123,地图,贴吧
t_list = bs.find_all(re.compile("\d"))#应用正则表达来输出包含数字的内容
for item in t_list :
print(item)
#输出:hao123
limit
t_list = bs.find_all("a",limit = 3)
for item in t_list :
print(item)
#输出三条标签为a的内容
css选择器
print(bs.select('title')) #通过标签查找
print(bs.select(".mnav")) #通过类名查找
print(bs.select("#u1")) #通过id查找
print(bs.select("a[class = bri]")) #通过属性查找
print(bs.select("head > title")) #查找head里面的title 通过子标签查找
t_list = bs.select(".mnav ~ .bri") #查找兄弟标签
print(t_list[0].get_text()) #文本
补充RE
基础匹配
正则表达式
正则表达式,又称规则表达式,是使用单个字符串来描述,匹配牧歌句法规则的字符串,常被用来检索,替换那些符合某个模式的文本
简单来说,正则表达式就是使用:字符串定义规则,并通过规则去验证字符串是否匹配,比如,验证一个字符串是否是符合条件的电子邮箱地址,至于要配置到正则规则,即可匹配任意邮箱。
比如通过正则规则:(^[\w-+(.[\w-]+)*@[\w-]+(.[\w-]+)+$)即可匹配一个字符串是否是标准邮箱格式
但如果不使用正则,对于if else来对字符串来做判断就非常困难了
正则的三个基础方法
python正则表达式,使用re模块,并基于re模块中三个基础方法来做正则匹配。
分别是:math,search,findall三个基础方法
match
- re.match(匹配规则,被匹配字符串)
从被匹配字符串开头进行匹配,匹配成功返回匹配对象(包含匹配信息),匹配不成功返回空
s = "python itheima python itheima python itheima"
result = re.match('python',s)
print(result)
#输出:<re.Match object; span=(0, 6), match='python'>
#span指的是匹配下标
#match指的是匹配内容
s = "1python itheima python itheima python itheima"
result = re.match('python',s)
print(result)
#输出None
#开头就不匹配直接返回None
search
- search(匹配规则,被匹配字符串)
搜索整个字符串,找出匹配的。从前往后,找到第一个后,就停止,不会继续向后
s = "1python666itheimma666python666"
result = re.search('python',s)
print(result)
#输出:<re.Match object; span=(1, 7), match='python'>
如果整个字符串都找不到,返回None
findall
- findall(匹配规则,被匹配字符串)
匹配整个字符串,找出全部匹配项
s = "1python666itheimma666python666"
result = re.findall('python',s)
print(result)
#输出:['python', 'python']
#如果找不到就返回一个空[]
元字符匹配
单字符匹配
字符 | 功能 |
---|---|
. | 匹配任意1个字符(除了\n,.匹配点本身) |
[ ] | 匹配[ ]中列举的字符 |
\d | 匹配数字0-9 |
\D | 匹配非数字 |
\s | 匹配空白,即空格,tab键 |
\S | 匹配非空白 |
\w | 匹配单词字符,即a-z,A-Z,0-9,_ |
\W | 匹配非单词字符 |
[^ ] | 匹配非[ ]中列举的字符 |
示例:
s = "itheima @@python2 !!666 ##itcast3"
#找出全部数字:
result1 = re.findall(r'\d',s)
print(result1)
#字符串的r标记,表示当前字符串是原始字符串,即内部的转义字符无效而是普通字符
#输出:['2', '6', '6', '6', '3']
#找到特殊字符
result2 = re.findall(r'\W',s)
print(result2)
#输出:[' ', '@', '@', ' ', '!', '!', ' ', '#', '#']
#找出全部英文字母
result3 = re.findall(r'[a-zA-z]',s)
print(result3)
#输出:['i', 't', 'h', 'e', 'i', 'm', 'a', 'p', 'y', 't', 'h', 'o', 'n', 'i', 't', 'c', 'a', 's', 't']
#[]内可以写[a-zA-Z0-9整个三种范围组合或指定单个字符如[aceDFG135]
数量匹配
字符 | 功能 |
---|---|
* | 匹配前一个规则的字符出现0至无数次 |
+ | 匹配前一个规则的字符出现1至无数次 |
? | 匹配前一个规则的字符出现0次或1次 |
{m} | 匹配前一个规则的字符出现m次 |
{m,} | 匹配前一个规则的字符出现最少m次 |
{m,n} | 匹配前一个规则的字符出现m到n次 |
边界匹配
字符 | 功能 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
\b | 匹配一个单词的边界 |
\B | 匹配非单词边界 |
分组匹配
字符 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
( ) | 将括号中字符作为一个分组 |
主要功能函数
函数 | 说明 |
---|---|
re.search() | 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象 |
re.match() | 从一个字符串的开始位置起匹配正则表达式,返回match对象 |
re.findall() | 搜索字符串,以列表类型返回全部能匹配的子串 |
re.spilt() | 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型 |
re.finditer | 搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素都是match对象 |
re.sub() | 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串 |
print(re.sub("a","A","abcdcasd")) # 找到a用A替换
re模式
正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位OR(|)他们来指定,如re.l|re.M被设置成I和M标志:
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响^和$ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Ucicode字符集解析字符,这个标志影响\w,\W,\b,\B |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写的更易于理解 |
修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位OR( | )他们来指定,如re.l |
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响^和$ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Ucicode字符集解析字符,这个标志影响\w,\W,\b,\B |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写的更易于理解 |