- 1. Tag 标签及其内容 默认拿到它所找到的第一个内容
- 如
- 2. NavigableString 标签里的内容(字符串)
- 百度一下,你就知道
- http://news.baidu.com‘, ‘name’: ‘tj_trnews’}">{‘class’: [‘mnav’], ‘href’: ‘http://news.baidu.com‘, ‘name’: ‘tj_trnews’}
- 3. BeautifulSoup 表示整个文档
- 4. Comment 是一个特殊的NavigableString,输出的内容不包括注释符号
爬虫:模拟浏览器打开网页,获取网页中我们想要的那部分数据
url :https://movie.douban.com/top250
任务:爬取豆瓣电影top250的基本信息,包括:电影名称、豆瓣评分、评价数、电影概况、电影链接
根据一个网页的链接,找到下一个网页:爬行——获取——爬行
搜索引擎:爬取+索引
基本流程
准备工作
通过浏览器查看分析目标网页,学习编程基础规范
获取数据
通过HTTP库向目标站点发起请求,请求可以包含额外的header信息,如果服务器能正常响应,会得到一个Response,便是多要获取的页面内容
解析内容
得到的内容可能是HTML,json等格式,可以用页面解析库,正则表达式等进行解析
保存数据
保存形式多样,可保存为文本,也可保存到数据库,或者保存特定格式的文件
- 网页解析
- userAgent: 表明你是一个什么版本的浏览器
- cookie:存储 读取,用于爬取登陆后才能获取的内容
- headers:我们向网页发送请求时,向服务器发送的消息,服务器通过headers验证我们的身份
学习urllib库
import urllib.request
## 获取一个get请求
response = urllib.request.urlopen("http://www.baidu.com")
## 返回一个对象,可以用read()函数获取它的内容,并且用decode来解码,
## 中文通常用utf-8
print(response.read().decode('utf-8'))
## 获取一个post请求,模拟用户登陆
## 测试网站:http://httpbin.org/
import urllib.parse
data = bytes(urllib.parse.urlencode({'hello':'world'}),encoding='utf-8') ## 传入用户名、密码
## 模拟浏览器发送请求,需要用post的方法来封装数据
response = urllib.request.urlopen('http://httpbin.org/post',data=data)
print(response.read().decode('utf-8'))
## time out 如果不响应,就pass
## 超时处理 try catch
try:
response = urllib.request.urlopen('http://httpbin.org/',timeout=0.01)
print(response.read().decode('utf-8'))
except urllib.error.URLError as e:
print('time out!')
# 测试豆瓣 418 被发现是爬虫,需要把url、headers等封装起来,伪装成一个浏览器
headers = {
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
}
url = 'https://www.douban.com'
## 封装一个请求,模拟浏览器发出这个请求。
req = urllib.request.Request(url=url,headers=headers,method='POST')
response = urllib.request.urlopen(req)
print(response.read().decode('utf-8'))
beautiful Soup 学习
- beautiful Soup中解析的数据类型 ```python “”” BeautifulSoup4将复杂的html文档转换成一个复杂的树形结构,每个节点都是python对象,所有对象可以 归纳成4种 -Tag -NavigableString ## 常用 -BeautifulSoup ## 常用 -Comment “”” from bs4 import BeautifulSoup file = open(‘../豆瓣/baidu.html’,’rb’) html = file.read() bs = BeautifulSoup(html,’html.parser’)
1. Tag 标签及其内容 默认拿到它所找到的第一个内容
如
print(bs.title) print(bs.a) print(bs.head)
print(bs.title.string) print(type(bs.title.string))
2. NavigableString 标签里的内容(字符串)
百度一下,你就知道
print(bs.a.attrs) ## 打印标签的属性,用字典存储
{‘class’: [‘mnav’], ‘href’: ‘http://news.baidu.com‘, ‘name’: ‘tj_trnews’}
3. BeautifulSoup 表示整个文档
print(type(ba)) # 一个类
4. Comment 是一个特殊的NavigableString,输出的内容不包括注释符号
print(bs.a.string) print(type(bs.a.string)
2. 文档的遍历
```python
#1.contents: 获取Tag 的所有子节点,返回一个list
print(bs.head.contents) ## 返回是head的内容,存储在一个list中,可以用下表取出
print(bs.head.contents[1]) ## 用列表索引来获取它的某一个元素
# 2. childern: 获取Tag 的所有子节点,返回一个生成器
for child in bs.body.children:
print(child)
# 3. decendants: 获取tag的所有子孙节点
# 4. strings :如果tag中包含多个字符串,即在子孙节点中所有内容,可以用此获取,然后进行遍历
# 5. stripped_strings : 与strings用法一致,只不过可以去除那些多余的空白内容
# 5. parent:获取tag的父节点
## 等各种乱七八糟的亲戚节点
文档的搜索
# 1. find_all() # 字符串过滤: 会查找与字符串完全匹配的的内容 返回一个list
t_list = bs.find_all('a')
print(t_list)
#2.正则表达式搜索: 使用search()方法搜索
import re
t_list= bs.find_all(re.compile("a"))
print(t_list) # 带a的都找出来了,需要精确匹配
# 3.方法 传入一个函数(方法),根据函数的要求来搜索
def name_is_exists(tag):
return tag.has_attr("name")
t_list = bs.find_all(name_is_exists) ## 函数作为一个参数传入
print(t_list)
for item in t_list:
print(item)
#4. kwargs 指定参数
t_list = bs.find_all(id='head')
t_list = bs.find_all(class_='True')
for item in t_list:
print(item)
#5. text 参数
t_list = bs.find_all(text=['hao123','地图','贴吧'])
t_list = bs.find_all(re.compile("\d")) #应用正则表达式来查找包含特定文本的内容(标签里的字符串)
for item in t_list:
print(item)
# 6.limit
t_list = bs.find_all('a',limit= 3) # 限制搜索返回个数
# 7. css选择器
t_list = bs.select('title')
t_list = bs.select('.mnav') # 按类名查找
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') # 按兄弟节点查找 manv 的bri的 兄弟
pring(t_list[0].get_text())
for item in t_list:
print(item)
正则表达式
ps:很多用户名要求大小写+_,因为,可以用\w很快匹配
import re
#创建模式对象
pat = re.compile("AA") # 此处的AA,是正则表达式,用来去验证其他的字符串
m = pat.search('CBAABDAACEG') # search字符串被校验的内容. search只返回第一个匹配的
print(m)
m = re.search('asd','Aasd') ## 前:模版 后:被校验的对象
print(m)
print(re.findall("a","ASEAaDNOa"))## 前:模版 后:被校验的对象, 返回list
print(re.findall("[A-Z]","ASEAaDNOa")) # 挑出所有大些字母
print(re.findall("[A-Z]+","ASEAaDNOa"))# 挑出所有大些字母组合
## sub
print(re.sub("a",'A','abcdliebowb')) # 找到a用A替换,在第三个字符串中替换
## 建议在正则表达式中,被比较的字符串前面加上r,不用担心转转义字符的问题
爬虫的全过程:
#-*- codeing = utf-8 -*_
#@Time: 2021/11/22
#@Author: yl-5940
#@File : douban.py
#@Software: PyCharm
from bs4 import BeautifulSoup ## 网页解析
import re ## 正则表达式,进行文字匹配
import xlwt ## 进行excel操作
import urllib.request,urllib.error ## 定制URL,获取网页数据
import sqlite3 ## 进行SQLite数据库操作
def main():
baseurl = 'https://movie.douban.com/top250?start='
##1,2.爬取网页、解析数据
datalist = getData(baseurl)
##3.保存数据
savepath = '豆瓣电影Top250.xls'
saveData(datalist,savepath)
## 4.保存到数据库
dbpath = 'movie.db'
saveData2DB(datalist,dbpath)
## 用正则匹配所需要获取的信息
# 影片详情链接的规则
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)
## 爬取网页
def getData(baseurl):
datalist = []
for i in range(0,10): ## 调用获取页面信息的函数,10次
url = baseurl + str(i*25)
html =askURL(url) ## 保存获取到的网页源码
## 2. 逐一解析, 采用BeautifulSoup
soup = BeautifulSoup(html,'html.parser')
for item in soup.find_all('div', class_="item"):## 查找符合要求的字符串,形成列表
#print(item) # 测试,查看电影item的全部信息
data =[]
item = str(item) ## 转换为str来采用正则
# 获取影片的超链接
link = re.findall(findLink,item)[0]
data.append(link)
# 获取影片的图片
imgSrc = re.findall(findImgSrc,item)[0]
data.append(imgSrc)
# 获取影片的名字
titles = re.findall(findTitle, item) ## 片名可能有中英文
if len(titles)==2:
ctitle = titles[0] # 添加中文名
data.append(ctitle)
otitle = titles[1].replace('/','') # 添加外文名
data.append(otitle)
else:
data.append(titles[0])
data.append(' ') ## 外国名留空、占位 很重要!
# 获取影片的评分
rating = re.findall(findRating,item)
data.append(rating)
# 获取影片的评价人数
judgeNum = re.findall(findJudge,item)
data.append(judgeNum)
# 获取影片的一句话评价
inq = re.findall(findInq,item) ## 有的inq可能是空的
if len(inq) !=0 :
inq = inq[0].replace('。','') # 去掉句号
data.append(inq)
else:
data.append(' ') ## 留空
bd = re.findall(findBd,item)[0]
bd = re.sub('<br(\s+)?/>(\s+)?'," ",bd) # 去掉.<br/>
bd = re.sub('/'," ",bd) # 替换 /
data.append(bd.strip()) # 去掉前后空格
datalist.append(data) ## 把处理好的一步电影的新型放入datalist
print(datalist)
return datalist
## 保存数据
def saveData(datalist,savepath):
book = xlwt.Workbook(encoding='utf-8',style_compression=0) ## 创建workbook对象
sheet = book.add_sheet('豆瓣电影Top250',cell_overwrite_ok=True) ## 创建工作表
col = ("电影详情链接",'图片链接','影片中文名','影片外文名','评分','评价人数','概况','相关信息')
for i in range(0,8):
sheet.write(0,i,col[i]) ## 写入列名
for i in range(0,250):
print("第%d条"%(i+1))
data = datalist[i]
for j in range(0,8):
sheet.write(i+1,j,data[j]) ##
book.save(savepath)
## 得到指定一个url网页内容
def askURL(url):
head = { ## 模拟浏览器头部信息,向豆瓣服务器发消息
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
} ## 用户代理,表示告诉豆瓣服务器,我们是什么类型的机器,浏览器(本质上是告诉浏览器我们可以接受什么格式的文件)
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:
if hasattr(e,'code'):
print(e.code)
if hasattr(e,'reason'):
print(e.reason)
return html
## 储存到数据库
def saveData2DB(datalist,dbpath):
init_db(dbpath)
conn = sqlite3.connect(dbpath)
cur =conn.cursor()
for data in datalist:
for index in range(len(data)):
#print(type(data[index]))
if type(data[index]) == list:## 评分和评价人数type是list, 不要换成str,否则无法写入
data[index] = data[index][0]
else:
data[index] = '"'+str(data[index]) +'"'
sql = '''
insert into movie250(
info_link,pic_link,cname,ename, score,rated,introduction,info)
values(%s)'''%",".join(data)
cur.execute(sql)
conn.commit()
cur.close()
conn.close()
def init_db(dbpath):
# 创建数据表
sql = '''
create table movie250
(
id integer primary key autoincrement,
info_link text,
pic_link text,
cname varchar,
ename varchar,
score numeric,
rated numeric,
introduction text,
info text
)
'''
conn = sqlite3.connect(dbpath)
cursor = conn.cursor()
cursor.execute(sql)
conn.commit()
conn.close()
if __name__ == "__main__" : ## 当程序执行时
## 调用函数 ,可以空值函数和代码的主流程
## 可以用于测试代码,让程序控制在此语句下
main()