爬虫_douban

豆瓣top250”网页数据的爬虫项目学习,已经忘记了80%,现在拿出来重新盘一下

一:发送请求

没登录情况下京东仍能保存收藏夹数据,126邮箱可以10天免登录?

说明信息不在服务器而是在本地,而且重启后仍有,说明信息存在硬盘上。

Cookie:93年网景(Netscape)公司发明的网络会话状态跟踪的技术

会话是由一组请求与响应组成,是围绕着一件相关事情进行的请求与响应。所以这些请求与响应之间一定需要有数据传递的,即是需要进行会话跟踪的。然而HTTP协议是一种无状态协议,在不同的请求间是无法进行数据传递的。此时就需要一种可以进行请求间数据传递的会话跟踪技术,即cookie!cookie是由服务器生成的,保存在客户端的一种信息载体。这个载体中存放着用户访问该站点的会话状态信息。只要cookie没被清空,或cookie没失效,保存在其中的会话状态就有效。用户提交第一次请求后,由服务器生成cookie,并将其封装到响应头中,以响应的形式发送给客户端。客户端接收到这个响应后,将cookie保存到本地。当客户端再发送同类请求后,在请求中会携带保存在客户端的cookie数据,发送到服务端,由服务器对会话进行跟踪。cookie是由若干键值对构成,cookie中的键值对均为字符串。

不同的浏览器,cookie的保存位置及查看方式不同。删除了某一浏览器下的cookie,不会影响其他浏览器中cookie浏览器选项—隐私—cookie—管理数据

2.headers

包含很多在向服务器提交请求时所附带的信息

\request headers:**

user agent:包含客户端的配置信息和浏览器版本等,让服务器判断请求的网页能不能兼容。即浏览器的身份。

Cookie:本地记录的浏览信息,包括登录信息,浏览信息,点击或由什么网页跳转过来等等。要是想做一些登录后才能看到的内容进行爬取,就需要用到存储和读取cookie。

\Response headers:**

服务器返回的信息,包括提交请求的时间等

3.HTTP和HTTPS协议

原文链接:https://blog.csdn.net/xiaoming100001/article/details/81109617

内容相当详细,这里不再赘述

4.get和post两种请求方法的区别

GET和POST是HTTP请求的两种基本方法,要说它们的区别,接触过WEB开发的人都能说出一二。

最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。

你可能自己写过无数个GET和POST请求,或者已经看过很多权威网站总结出的他们的区别,你非常清楚知道什么时候该用什么。

当你在面试中被问到这个问题,你的内心充满了自信和喜悦。

你轻轻松松的给出了一个“标准答案”:

  • GET在浏览器回退时是无害的,而POST会再次提交请求。
  • GET产生的URL地址可以被Bookmark,而POST不可以。
  • GET请求会被浏览器主动cache,而POST不会,除非手动设置。
  • GET请求只能进行url编码,而POST支持多种编码方式。
  • GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
  • GET请求在URL中传送的参数是有长度限制的,而POST么有。
  • 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
  • GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
  • GET参数通过URL传递,POST放在Request body中。

(本标准答案参考自w3schools)

很遗憾,这不是我们要的回答!”

请告诉我真相。。。

如果我告诉你GET和POST本质上没有区别你信吗?

让我们扒下GET和POST的外衣,坦诚相见吧!

GET和POST是什么?HTTP协议中的两种发送请求的方法。

HTTP是什么?HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。

HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。

那么,“标准答案”里的那些区别是怎么回事?

在我大万维网世界中,TCP就像汽车,我们用TCP来运输数据,它很可靠,从来不会发生丢件少件的现象。但是如果路上跑的全是看起来一模一样的汽车,那这个世界看起来是一团混乱,送急件的汽车可能被前面满载货物的汽车拦堵在路上,整个交通系统一定会瘫痪。为了避免这种情况发生,交通规则HTTP诞生了。HTTP给汽车运输设定了好几个服务类别,有GET, POST, PUT, DELETE等等,HTTP规定,当执行GET请求的时候,要给汽车贴上GET的标签(设置method为GET),而且要求把传送的数据放在车顶上(url中)以方便记录。如果是POST请求,就要在车上贴上POST的标签,并把货物放在车厢里。当然,你也可以在GET的时候往车厢内偷偷藏点货物,但是这是很不光彩;也可以在POST的时候在车顶上也放一些数据,让人觉得傻乎乎的。HTTP只是个行为准则,而TCP才是GET和POST怎么实现的基本。

但是,我们只看到HTTP对GET和POST参数的传送渠道(url还是requrest body)提出了要求。“标准答案”里关于参数大小的限制又是从哪来的呢?

在我大万维网世界中,还有另一个重要的角色:运输公司。不同的浏览器(发起http请求)和服务器(接受http请求)就是不同的运输公司。 虽然理论上,你可以在车顶上无限的堆货物(url中无限加参数)。但是运输公司可不傻,装货和卸货也是有很大成本的,他们会限制单次运输量来控制风险,数据量太大对浏览器和服务器都是很大负担。业界不成文的规定是,(大多数)浏览器通常都会限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url。超过的部分,恕不处理。如果你用GET服务,在request body偷偷藏了数据,不同服务器的处理方式也是不同的,有些服务器会帮你卸货,读出数据,有些服务器直接忽略,所以,虽然GET可以带request body,也不能保证一定能被接收到哦。

好了,现在你知道,GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。

你以为本文就这么结束了?

我们的大BOSS还等着出场呢。。。

这位BOSS有多神秘?当你试图在网上找“GET和POST的区别”的时候,那些你会看到的搜索结果里,从没有提到他。他究竟是什么呢。。。

GET和POST还有一个重大区别,简单的说:

GET产生一个TCP数据包;POST产生两个TCP数据包。

长的说:

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);

而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。

因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?

  1. GET与POST都有自己的语义,不能随便混用。

  2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。

  3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

    现在,当面试官再问你“GET与POST的区别”的时候,你的内心是不是这样的?

    结束!!!

原文:https://www.cnblogs.com/logsharing/p/8448446.html

二:数据清洗

1.urllib的使用方法

\获取get请求:**

from urllib import request

1
2
3
Response = request.urlopen(“www.baidu.com”)
Print(Response.read().decode(UTF-8))
#response是对象类型,通过read()读取对象内容,decode()实现对网页源码的以UTF-8解码

\获取post请求:**

from urllib import parse

1
2
3
4
5
#填写一个键值对并转为二进制编码作为表单
Data = bytes(parse.urlencode({“hellow”:”world”},encoding=”utf-8”))
#提交post时附上表单内容,真正要实现代替浏览器登录的话有时还需要cookie内容
response = request.urlopen(‘http://httpbin.org/post’,data=Data)
print(response.read().decode(UTF-8))

\超时处理:**

1
2
3
4
5
6
#引入异常处理,当网页由于各种原因无法访问时,需要设置延迟时间并制定下一步动作
try
response = request.urlopen(‘http://httpbin.org/get’,timeout=0.1
print(response.read().decode(UTF-8))
except urllib.error.UTLError as e:
pass

\反反爬第一步 (error418)如何解决:**

Response = request.urlopen(“www.douban.com”)会报错,是因为直接使用该方法时,请求头中的

user-agent:python;解决方法就是要模拟浏览器登录。

复制消息头-请求头中的use-agent数据

1
2
3
4
5
6
#写入请求头信息
header={“use-agent”:“Mozilla/5.0..........”) Gecko/20100101 Firefox/77.0”}
#在获取请求时构造请求头信息
Req = request.Request(url=””, date=””, headers=header, method=”post”)
#method 默认是get请求
Reponse = request.urlopen(Req)

此时打印Reponse就能看到网页代码

2.如何获取网页源码(单个网页)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#定义获取单个网页的函数,将被访问的url作为参数传入
def askurl(need_url):
#复制本机用户代理信息
header={“use-agent”:“Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0”}
#添加请求头和请求方法
req = request.Request(url = need_url,headers = header,method = 'post')
response = request.openurl(req)
#也可以引入异常处理机制
try:
respones = request.urlopen(req)
html = respones.read().decode("utf-8")
except u.error.URLError as e:
if hasattr(e,"code"):
print(e.code)
if hasattr(e,"reason"):
print(e.reason)
#返回网页源码
return html

3.如何从html中获得需要信息

需要用到的库beautifulsoup+re:

Beautiful Soup :是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据。

re:是Python的标准库,主要通过正则表达式实现字符串匹配。

1.先将html文件通过BeautifulSoup解析,返回一个解析后的对象

2.调用该对象的find_all方法,通过标签名,id,类名等得到我们想要的模块

3.将得到的模块转换成字符串格式

4.正则提取

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
#1.定义获取数据信息的函数,以网页源码html作为参数
def getdata(html)
#定义空列表盛放全部电影的数据
datalist = []
#2.找到需要的信息模块(通过网页代码看到item包含了我们需要的信息)
bs = BeautifulSoup(html, "html.parser")
for items in bs.find_all("div",class_= "item"):
#更改为字符串格式,方便正则提取
items = str(items)
#3.先看下面的通过正则定义提取哪些变量“定义变量的正则格式:”
#4.正则提取所需信息,注意!!findall()返回列表,且有些内容可能为空,需要检测
name = re.findall(moviename,items)
print(len(name))#可以看到有1个或者2个(有的还带外文名,又是一个span)
if len(name) == 1
chname = name[0]
abname = ' ' #空格号填充
else:
chname = name[0]
abname = name[1]
othername = re.findall(othername,items)[0]
imglink = re.findall(img,items)[0]
link = re.findall(infolink,items)[0]
cast = re.findall(cast, item)[0]
score = re.findall(score, item)[0]
count = re.findall(num, item)[0]
inq = re.findall(comment, item)
if len(inq) == 0:
inq = ' '
else:
inq = inq[0].replace('。',''
data = [] #定义空列表盛放单个电影的数据
#5.将本次循环的单个电影的参数放入data,下次循环data又为空
data.extend([chname,abname,othername,imglink,link,cast,score,count,ing])
#先打印出来预览一下,有乱码的部分还要修改,见线面“根据展示的结果进行相应修改”
#将data放入datalist,每次循环放入一个电影的data
datalist.append(data)
#获得一个html文件的全部需要数据
return datalist
1
2
3
4
5
6
7
#根据展示的结果进行相应修改
abname = name[1].replace('/','') #字符串的替换
abname = re.sub('\s','',abname,2) #替换abname的内容,前两个空格去电
othername = re.sub('\s','',othername) #去除空格
othername = re.sub('/','',othername,1) #去除第一个斜杠
cast = re.sub('<br(\s*)?/(\s*)?>', '', cast).strip() #先替换内容,再把首位空格或功能符都删了
cast = re.sub('\s','',cast) #删除全部的空格
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#定义变量的正则格式:
#1.电影名:
moviename = re.compile(r'<span class="title">(.*)</span>')
#不带‘?’,非贪婪模式,满足<span class="title">(.*)</span>的我都要
#2.别名:
othername = re.compile(r'<span class="other">(.*?)</span>')
#带‘?’,贪婪模式,满足<span class="title">(.*)</span>的我只要第一个
#3.详情链接:
infolink = re.compile(r'<a href="(.*?)">')
#4.海报图片:
img = re.compile(r'<img alt=".*" class="" src="(.*?)" width="100"/>',re.S)
#5.演员阵容:
cast = re.compile(r'<p class="">(.*?)</p>', re.S)
#6.评分:
score = re.compile(r'<span class="rating_num" property="v:average">(.*?)</span>')
#7.评价人数:
num = re.compile(r'<span>(\d*人评价)</span>')
#8.评语:
comment = re.compile(r'<span class="inq">(.*?)</span>')

4.循环所有网页

循环所有网页,获取每页的html,对每次得到的html文件执行getdata()函数,放入alldata列表。

首先要找出我们需要爬取的网页有哪些共同点,即如何设计循环:豆瓣250的每次翻页,改变的都是一个数字,这就是切入点

1
2
3
4
5
6
7
8
9
10
#在上一基础上定义获取全部网页源码的函数,以baseurl作为参数,方便复用,再次调用函数只需要改变循环方式即可
def merge(baseurl):
dataall = []
for i in range(10):
url = baseurl + str(i*25)
html = askurl(url)
datalist = getdata(html)
#将一个网页中的每个data放入dataall
dataall.extend(datalist)
return dataall

三:数据保存

1.excel表格

使用的第三方库:xlwt,实现对excel表格的写入

步骤:

1.以utf-8编码格式,创建一个xls工作文件(内存)

excelbook = xlwt.Workbook(encoding = "utf-8")

2.在工作文件内创建工作表

excelsheet = excelbook.add_sheet("sheet1")

3.向工作表内写入数据

excelsheet.write(y, x, "data")

y决定纵轴的位置,x决定横轴的位置,即将data即写入第y行x列。

4.保存工作表到本地

excelbook.save("d:\..\..\..\movie250.xls")

具体代码如何将datalist中的每个data放入对应位置呢?

1
2
3
4
5
6
7
8
book = xlwt.Workbook(encoding = 'utf-8')
sheet = book.add_sheet("sheet1")
title = ['chname', 'abname', 'othername', 'imglink', 'link', 'cast', 'score', 'count','ing']
for i in range(8):
sheet.write(i,0,title[i])
for j in range(len(datalist)):
sheet.write(i,j+1,datalist[j][i])
book.save("movie250.xls")

2.sqlite数据库

使用第三方库:sqlite3,实现导入数据库,后续数据库学习-MySQLite

辅助工具:Database Navigator,实现数据库预览和编辑,类似excel。社区版安装方法

步骤:

2.1初始化数据库(title)

1.链接数据库,没有就新建一个

con = sqlite3.connect(path)

2.创建光标

cur = con.cursor()

3.sql语句

1
2
3
4
5
6
7
8
9
10
11
12
13
sql = '''
create table movie250 #创建新表单(类似于sheet)
id int primary key autoincrement #标签,primary key:组件,autoincrement:自增长
chname varchar, #变长字符串,可排序
abname varchar,
othername varchar,
imglink varchar,
link varchar,
cast varchar,
score varchar,
count text,
ing varchar,
'''

4.执行语句并提交到本地

data = cur.execute(sql)

con.commit()

5.关闭连接

cur.close()

con.close()

2.2将内容填入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def save_sqlite(dataall,path):
init_sqlite(path)
con = sqlite3.connect(path)
cur = con.cursor()
#同样是遍历dataall,操作每个电影的data
for data in dataall:
for i in range(len(data)):
data[i] = '"'+data[i]+'"'
data = ','.join(data) #!!!!!字符串拼接打法,只要是多个元素的对象都能拼
sql = f'''
insert into movie250 (chname, abname, othername, img, link, cast, score, count, inq)
values({data})
'''
cur.execute(sql)
con.commit()
cur.close()
con.close()

2.3 database navigator预览

1.社区版安装完成后,顶部工具栏出现DB Navigator选项

2.Database Browser可以打开预览侧边栏

3.点击“+”,选择SQLite数据库打开,在database files处选择db文件路径

4.通过这一工具也可以增加或删减行

四:数据可视化

1.html表格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.py: 从数据库中遍历数据,作为参数传入html文件
from flask import Flask,render_template
import sqlite3
@app.route('/movie') def movie(): #分配路由+定义函数
con = sqlite3.connect("豆瓣电影top250.db") #连接数据库
cur = con.cursor() #创建光标
sql = '''
select * from movie250 #从数据库表单中调取所有数据
'''
data = cur.execute(sql) #执行数据库命令将数据拿出,不然关闭数据库的时候从内存删除了
#将data从对象转成列表
datalist = []
for items in data:
datalist = datalist.append(items)
cur.close()
con.close()
return render_template("movie.html",movies = datalist)#选择需要渲染的网页,将参数传递到html
1
2
3
4
5
6
7
8
9
10
11
12
.html: 通过循环将信息填入,将信息展示出来
<table>
{% for movie in {{movies}} %} //对传入的电影信息遍历,获得单个电影列表
将每个电影的信息填入表格
<tr>
<td>{{movie[1]}}</td>
<td>{{movie[2]}}</td>
<td>{{movie[3]}}</td>
...
</tr>
{% endfor %}
</table>

2.Echarts统计图

就是通过引入别人设计好的 js 模板,再把我们已有的数据传入,生成图表

2.1.安装:Echarts官网进入下载页

我是选择方法一,进入github链接,找到文件所在仓库,直接clone到本地

方法三提供了在线定制的选项,可以直接选择自己需要的图形样式进行下载

2.2使用:5 分钟上手 ECharts

官方提供了实例模板,CV大法拷贝到本地运行看能不能出来效果,实际使用时注意将html文件和需要用到的js文件分别放入指定文件夹templates和static,并且修改html文件中链接js文件的相对地址

将html文件中的标题,横轴,纵轴等参数进行修改就能得到我们想要的图标样式

3.3将数据库信息通过图标呈现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.py:
from flask import Flask,render_template
import sqlite3
@app.route('/score') def movie(): #分配路由+定义函数
con = sqlite3.connect("movie250.db") #连接数据库
cur = con.cursor() #创建光标
sql = '''
#从数据库表单中调取评分和对应评分下的电影数量,按照评分进行分组
select score,count(score) from movie250 group by score
'''
data = cur.execute(sql) #执行数据库命令将数据拿出,不然关闭数据库的时候从内存删除了
score = []
count = []
for items in data: #此时每次遍历的items就是“评分”和“电影数”组成的一个列表
score.append(items[0]) #把评分提取组成列表,作为横轴
count.append(items[1]) #对应电影数组成列表,作为纵轴
cur.close()
con.close()
return render_template("movie.html",score=score,count=count)
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
.html:
头部引入js文件:<script src="static/assets/js/echarts.min.js"></script>
<body>
<!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var option = {
title: {
text: '评分'
},
tooltip: {},
legend: {
data:['电影数']
},
xAxis: {
data: {{score|tojson}}
//不加|tojson可以看到网页源码这里的数据就是一串乱码,加上就能识别为字符串格式
},
yAxis: {},
series: [{
name: '数量',
type: 'bar',
data: {{count}}
}]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>

报错1:第一次直接写的

1
{{score}}

网页不显示图形:

解决:查看网页源码,发现横轴data对应的数据是一串乱码,改为

1
{{score|tojson}}

后乱码消失