- 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-8print(response.read().decode('utf-8'))## 获取一个post请求,模拟用户登陆## 测试网站:http://httpbin.org/import urllib.parsedata = 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 catchtry: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 的所有子节点,返回一个listprint(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() # 字符串过滤: 会查找与字符串完全匹配的的内容 返回一个listt_list = bs.find_all('a')print(t_list)#2.正则表达式搜索: 使用search()方法搜索import ret_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.limitt_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"))## 前:模版 后:被校验的对象, 返回listprint(re.findall("[A-Z]","ASEAaDNOa")) # 挑出所有大些字母print(re.findall("[A-Z]+","ASEAaDNOa"))# 挑出所有大些字母组合## subprint(re.sub("a",'A','abcdliebowb')) # 找到a用A替换,在第三个字符串中替换## 建议在正则表达式中,被比较的字符串前面加上r,不用担心转转义字符的问题
爬虫的全过程:
#-*- codeing = utf-8 -*_#@Time: 2021/11/22#@Author: yl-5940#@File : douban.py#@Software: PyCharmfrom 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. 逐一解析, 采用BeautifulSoupsoup = 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) ## 把处理好的一步电影的新型放入datalistprint(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()
