HTTP
HTTP是一个客户端(用户)和服务器端(网站)之间进行请求和应答的标准。通过使用网页浏览器、网络爬虫或者其他工具,客户端可以向服务器上的指定端口(默认端口为80)发起一个HTTP请求。这个客户端成为客户代理(user agent)。应答服务器上存储着一些资源码,比如HTML文件和图像。这个应答服务器成为源服务器(origin server)。在用户代理和源服务器中间可能存在多个“中间层”,比如代理服务器、网关或者隧道(tunnel)。尽管TCP/IP是互联网最流行的协议,但HTTP中并没有规定必须使用它或它支持的层。
事实上。HTTP可以在互联网协议或其他网络上实现。HTTP假定其下层协议能够提供可靠的传输,因此,任何能够提供这种保证的协议都可以使用。使用TCP/IP协议族时RCP作为传输层。通常由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的TCP链接。HTTP服务器则在该端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态(比如“THTTP/1.1 200 OK”),以及请求的文件、错误信息等响应内容。
HTTP的请求方法有很多种,主要包括以下几个:
-
GET:向指定的资源发出“显示”请求。GET方法应该只用于读取数据,而不应当被用于“副作用”的操作中(例如在Web Application中)。其中一个原因是GET可能会被网络蜘蛛等随意访问。
-
HEAD:与GET方法一样,都是向服务器发出直顶资源的请求,只不过服务器将不会出传回资源的内容部分。它的好处在于,使用这个方法可以在不必传输内容的情况下,将获取到其中“关于该资源的信息”(元信息或元数据)。
-
POST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求文本中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。
-
PUT:向指定资源位置上传输最新内容。
-
DELETE:请求服务器删除Request-URL所标识的资源,或二者皆有。
-
TRACE:回显服务器收到的请求,主要用于测试或诊断。
-
OPTIONS:这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用“*”来代表资源名称向Web服务器发送OPTIONS请求,可以测试服务器共能是否正常。
-
CONNECT:HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的连接(经由非加密的HTTP代理服务器)。方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed),当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)。
网页基础
网页组成
我们的数据来源是网页,那么我们在真正抓取数据之前,有必要先了解一下一个网页的组成。
网页是由 HTML 、 CSS 、JavaScript 组成的。
HTML 是用来搭建整个网页的骨架,而 CSS 是为了让整个页面更好看,包括我们看到的颜色,每个模块的大小、位置等都是由 CSS 来控制的, JavaScript 是用来让整个网页“动起来”,这个动起来有两层意思,一层是网页的数据动态交互,还有一层是真正的动,比如我们都见过一些网页上的动画,一般都是由 JavaScript 配合 CSS 来完成的。
使用开发者工具检查网页
Chrome的开发者模式为用户提供了下面几组工具。
Elements:允许用户从浏览器的角度来观察网页,用户可以借此看到Chrome渲染页面所需要的HTML、CSS和DOM(Document Object Model)对象。
Network:可以看到网页向服务气请求了哪些资源、资源的大小以及加载资源的相关信息。此外,还可以查看HTTP的请求头、返回内容等。
Source:即源代码面板,主要用来调试JavaScript。
Console:即控制台面板,可以显示各种警告与错误信息。在开发期间,可以使用控制台面板记录诊断信息,或者使用它作为shell在页面上与JavaScript交互。
Performance:使用这个模块可以记录和查看网站生命周期内发生的各种事情来提高页面运行时的性能。
Memory:这个面板可以提供比Performance更多的信息,比如跟踪内存泄漏。
Application:检查加载的所有资源。
Security:即安全面板,可以用来处理证书问题等。
另外,通过切换设备模式可以观察网页在不同设备上的显示效果,快捷键为:Ctrl + Shift + M(或者在 Mac上使用 Cmd + Shift + M),
requests.get
import requests
url = r'https://www.python.org/dev/peps/pep-0020/'
res = requests.get(url)
text = res.text
text
print(text[text.find('<pre')+28:text.find('</pre>')-1])
requests.post
# 利用金山词霸来翻译我们刚刚爬出来的python之禅
import requests
def translate(word):
url="http://fy.iciba.com/ajax.php?a=fy"
data={
'f': 'auto',
't': 'auto',
'w': word,
}
# User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36',
}#User-Agent会告诉网站服务器,访问者是通过什么工具来请求的,如果是爬虫请求,一般会拒绝,如果是用户浏览器,就会应答。
response = requests.post(url,data=data,headers=headers) #发起请求
json_data=response.json() #获取json数据
#print(json_data)
return json_data
def run(word):
result = translate(word)['content']['out']
print(result)
return result
def main():
txt = res[res.find('<pre')+28:res.find('</pre>')-1].split('\n')
zh = [run(word) for word in txt]
if __name__ == '__main__':
main()
爬取豆瓣电影:
# 爬取豆瓣电影
import urllib
import requests
from lxml import etree
import os
# 定义一个空列表用于保存十个页面的地址
url_list = []
def get_url():
# 十个页面的增加规律是每次加25
for i in range(0, 226, 25):
url_root = "https://movie.douban.com/top250?start=%d&filter=" % i
# 将形成的网页地址添加到列表中
url_list.append(url_root)
# 定义两个空列表,一个用于保存名称,另一个用于保存图片地址
title_list = []
image_url_list = []
def get_title(url, headers):
res = requests.get(url=url, headers=headers)
# 这里采用etree来解析xml会方便许多
html = etree.HTML(res.text)
# 匹配名称所在的路径
title = html.xpath("//img/@alt")
for i in range(0, len(title) - 1):
# 将提取到的名称保存到名称列表
title_list.append(title[i])
def get_image_url(url, headers):
res = requests.get(url=url, headers=headers)
html = etree.HTML(res.text)
img_url = html.xpath("//img/@src")
for i in range(0, len(img_url) - 1):
image_url_list.append(img_url[i])
def main():
headers = {
"Host": "movie.douban.com",
"Referer": "https://movie.douban.com/top250?start=225&filter=",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36",
}
# 先获得十个页面的地址
get_url()
# 调用函数得到相应的列表
for url in url_list:
get_title(url, headers)
get_image_url(url, headers)
if os.path.exists("name_pic.txt") == True:
os.remove("name_pic.txt")
f = open("name_pic.txt", "a")
for word in zip(title_list,image_url_list):
f.write(str(word) + "\n")
f.close()
movie_path = "movie_img"
if movie_path not in os.listdir():
os.makedirs(movie_path)
for i in range(0, len(title_list)):
for j in range(0, len(image_url_list)):
if(i == j):
movie_name = title_list[j] + ".jpg"
filename = movie_path + "/" + movie_name
# 将图片下载到本地,并命名
print(movie_name)
urllib.request.urlretrieve(url=image_url_list[i], filename=filename)
if __name__ == '__main__':
main()
JavaScript与AJAX技术
果利用Requests库和BeautifulSoup来采集一些大型电商网站的页面,可能会发现一个令人疑感的现象,那就是对于同一个URL、同一个页面,抓取到的内容却与浏览器中看到的内容有所不同。比如有的时候去寻找某一个
为了避免为每一份要呈现的网页内容都准备一个HTML,网站开发者们开始考虑对网页的呈现方式进行变革。在JavaScript问世之初,Google公司的Gmail邮箱网站是第一个大规模使用JavaScript加载网页数据的产品,在此之前,用户为了获取下一页的网页信息,需要访问新的地址并重新加载整个页面。但新的Gmail则做出了更加优雅的方案,用户只需要单击“下一页”按钮,网页就(实际上是浏览器)会根据用户交互来对下一页数据进行加载,而这个过程并不需要对整个页面(HTML)的刷新。换句话说,JavaScript使得网页可以灵活地加载其中一部分数据。后来,随着这种设计的流行,“AJAX”这个词语也成为一个“术语”,Gmail作为第一个大规模使用这种模式的商业化网站,也成功引领了被称之为“Web2.0”的潮流。
JavaScript语言一般被定义为一种“面向对象、动态类型的解释性语言”,最初由Netscape公司为Navigator浏览器开发,目的是作为新一代浏览器的脚本语言支持。换句话说,不同于PHP或者ASP.NET,JavaScript不是为“网站服务器”提供的语言,而是为“用户浏览器”提供的语言。从客户端——服务器端的角度来说,JavaScript无疑是一种客户端语言,但是由于JavaScript受到业界和用户的强烈欢迎,加之开发者社区的活跃,目前的JavaScript已经开始朝向更为综合的方问发展。随着V8引擎(可以提高JavaScript的解释执行效率)和Node.js等新潮流的出现,JavaScript甚至已经开始涉足“服务器端”。在TIOBE排名(一个针对各类程序设计语言受欢迎度的比较)上,JavaScript稳居前10,井与PHP、Pytbon、C#等分庭抗礼。 有一种说法是,对于今天任何一个正式的网站页面而言,HTML决定了网页的基本内容,CSS(Cascading Style Sheets,层叠样式表)描述了网页的样式布局,JavaScript 则控制了用户与网页的交互。
AJAX
AJAX技术与其说是一种“技术”,不如说是一种“方案”。如上文所述,在网页中使用JavaScript 加载页面中数据的过程,都可以看作AJAX技术。AJAX技术改变了过去用户浏览网站时一个请求对应一个页面的模式,允许浏览器通过异步请求来获取数据,从而使得一个页面能够呈现并容纳更多的内容,同时也就意味着更多的功能。只要用户使用的是主流的浏览器,同时允许浏览器执行JavaScript,用户就能够享受网页中的AJAX内容。
AJAX技术在逐渐流行的同时,也面临着一些批评和意见。由于JavaScript本身是作为客户端脚本语言在浏览器的基础上执行的,因此,浏览器兼容性成为不可忽视的问题。另外,由于JavaScript在某种程度上实现了业务逻辑的分离(此前的业务逻辑统一由服务器端实现),因此在代码维护上也存在一些效率问题。但总体而言,AJAX技术已经成为现代网站技术中的中流砥柱,受到了广泛的欢迎。AJAX目前的使用场景十分广泛,很多时候普通用户甚至察觉不到网页正在使用AJAX技术。 以知乎的首页信息流为例,与用户的主要交互方式就是用户通过下拉页面(具体操作可通过鼠标滚轮、拖动滚动条等实现)查看更多动态,而在一部分动态(对于知乎而言包括被关注用户的点赞和回答等)展示完毕后,就会显示一段加载动画并呈现后续的动态内容。此处的页面动画其实只是“障眼法”,在这个过程中,JavasScript脚本已向服务器请求发送相关数据,并最终加载到页面之中。这时页面显然没有进行全部刷新,而是只“新”刷新了一部分,通过这种异步加载的方式完成了对新内容的获取和呈现,这个过程就是典型的AJAX应用。
比较尴尬的是,爬虫一般不能执行包括“加载新内容”或者“跳到下一页”等功能在内的各类写在网页中的JavaScript代码。如本节开头所述,爬虫会获取网站的原始HTML页面,由于它没有像浏览器一样的执行JavaScript脚本的能力,因此也就不会为网页运行JavaScript。最终,爬虫爬取到的结果就会和浏览器里显示的结果有所差异,很多时候便不能直接获得想要的关键信息。为解决这个尴尬处境,基于Python编写的爬虫程序可以做出两种改进,一种是通过分析AJAX内容(需要开发者手动观察和实验),观察其请求目标、请求内容和请求的参数等信息,最终编写程序来模拟这样的JavaScript 请求,从而获取信息(这个过程也可以叫作“逆向工程”)。另外一种方式则比较取巧,那就是直接模拟出浏览器环境,使得程序得以通过浏览器模拟工具“移花接木”,最终通过浏览器渲染后的页面来获得信息。这两种方式的选择与JavaScript在网页中的具体使用方法有关。
Xpath基础
Xpath 是一门从html中提取数据的语言:
Xpath的语法:
-
‘/‘是选择节点(标签):’ /html/head/meta’ :表示的是能够选中html 下的head下的所有的meta 标签
-
‘//’:能够从任意节点开始选择 ‘//li’: 表示的是当前页面上的所有li 标签 ‘/html/head//link’ :表示的是head 下的所有的link标签,head下的子节点和子孙节点的所有的link标签。
-
‘@符号的用法’: 选择具体某个元素 ‘//div[@class=‘xxx’]/ul/li’ 选择‘xxx’的div下的ul下的li 那个‘[]’表示的是选中一个区块,然后再在这个区块(‘xxx’)下选择其他的标签。‘a/@href’ :表示的是选择a的href的值
-
获取文本用‘/a/text()’ :获取a下的文本;‘/a//text()’: 获取a下的所有的文本;从一个节点选到另外一个节点用的是‘/’ ,从根节点选择的时候也是用‘/’ , 从任意节点选择的话就用‘//’,这个也表示选择全部。
‘./’ 表示的是当前标签下的 比如table.xpath(".//div") ,表示的是当前table下的div
参考:
https://www.bilibili.com/video/BV1zW411j7Po?p=1
https://www.bilibili.com/video/BV1NW411V7CQ?p=1