1.爬虫基本介绍
目录
1 爬虫介绍
1.1 爬取流程
爬取的都是http/https的数据,移动端的数据,发送请求获取数据,并不是只有python能做爬虫(任何语言都可以做爬虫),python比较便捷,模块多,上手快,爬虫框架scrapy
发送http请求(requests模块)-----》服务端返回数据(咱们要爬取的网站)-----》拿到数据解析(re,bs4)(json/html)------》入库保存 (文件,mysql,redis) ------》后续可以做分析
发送http请求:浏览器,浏览器会自动组装数据包(http的请求包),向服务端发送请求
自己基于socket来封装,比较复杂,原生python提供了urllib模块,有个人基于urllib模块继续封装,做了requests模块,
使用requests模块模拟发送http请求,有的时候爬不到数据,被禁止了,一定是你模拟的不够像,
可见即可爬
爬虫协议:规定了什么东西我让你爬,什么东西不让你爬:https://www.baidu.com/robots.txt
状态码301和302的区别?
301 redirect: 301 代表永久性转移(Permanently Moved)
302 redirect: 302 代表暂时性转移(Temporarily Moved )
详细来说,301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。他们的不同在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。
2 requests模块
Django的request对象里有个META字典,请求头中的所有东西,都在这个字典中
监听 127.0.0.1:8080和0.0.0.0:8080的区别,前者只允许本机访问,后者允许任意机器访问
ALLOWED_HOSTS = ['*'] 程序层面,允许任意ip地址访问
请求头的数据从request.META 中取,只不过前面加了个HTTP,把请求头的key值变为了大写
问题 :post请求在请求地址中可以不以带请求参数?如果带了,从哪拿?
连接地址中的从request.GET,如果在请求体中的参数在request.POST中
三种编码格式:urlencoded,json,form-data
django request.POST中之所有能取出来数据来,因为django框架从请求体中取出数据,放到POST中去了,编码格式必须是urlencoded
如果编码格式是json格式,django不干这个事,request.POST中取不出来,request.body取出来中自行处理
http请求体:
urlencoded:name=wang&age=19
json:{"name":"刘清政","":""}
form-data:很复杂
模拟发送http请求
# pip3 install requests
# 导入
import requests
# 用的最多的就是get和post
# requests.request('get','地址') get和post请求本质就是它
#1 ret就是响应对象,response对象
ret=requests.get('https://www.baidu.com')
# 响应体的内容
print(ret.text)
with open('baidu.html','w') as f:
f.write(ret.text)
需要向携带头部的地址发送请求
# User-Agent标志是什么客户端
ret=requests.get('https://www.baidu.com',
headers={
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36',
'':''
})
# 响应体的内容
print(ret.text)
with open('baidu.html','w') as f:
f.write(ret.text)
自己服务器模拟requests使用
ret=requests.get('http://127.0.0.1:8001/',
headers={
'aaa':'xxx'
})
print(ret.text)
get请求携带参数
#第一种方式
ret=requests.get('http://127.0.0.1:8001/?name=刘清政&age=18',
)
#第二种方式(推荐使用)
ret=requests.get('http://127.0.0.1:8001/',params={'name':'zhang','age':19})
#有什么区别
#第二种方式,如果有中文,会自动转码
# https://www.baidu.com/s?wd=%E7%BE%8E%E5%A5%B3
#补充,可以通过urlencode进行转码
from urllib.parse import urlencode
print(urlencode({'name':'zhang'},encoding='utf-8'))
print(ret.text)
get请求携带header中的referer
#Referer,一般网站通过这个东西做反扒,上一个地址是什么
#图片防盗链
#www.aaa.top 偷了cnblogs一个图片
# img referer:www.aaa.top
ret=requests.get('https://www.baidu.com',
headers={
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36',
'Referer':'',
})
携带cookie的两种方式
#第一种方式
ret=requests.get('http://www.baidu.com',
headers={
'cookie':'key:value;key2:value2'
})
# #第二种方式(推荐)
ret=requests.get('http://www.baidu.com',
cookies={"key":'value','key2':'value21'}
)
发送post请求携带数据的位置
# 两个位置,一个是请求地址中,一个是请求体中
#params:放到地址中的参数
# data:请求体中的参数
ret=requests.post('http://127.0.0.1:8001/',params={'name':'wang','age':18},data={'name':'zhang','age':17})
print(ret)
响应对象response
ret=requests.post('http://127.0.0.1:8001/',params={'name':'wang','age':18},data={'name':'zhang','age':17})
print(ret.text)
print(ret.content) # 响应体的二进制格式,一旦请求的是图片。。。,
# print(ret.status_code) #响应的状态码
print(ret.cookies) #对象,requests模块自己封装的对象
print(ret.cookies.get_dict()) #这个是字典
print(ret.url)
#列表,放的是重定向之前的response对象
print(ret.history[0].url)
# ret.encoding 服务端的编码方式
# 重点
# session=requests.sessions()
#再发送各种请求,不需要手动加cookie了,它自动处理了
解析json
# 在实际项目中遇到的问题
ret=requests.post('http://127.0.0.1:8001/')
print(type(ret.text)) #str类型
print(type(ret.json())) #字典类型
# python 3.5 和python3.6以后 json 稍微改动了一下,3.5以前只能传 (json.loads(字符串)) 3.6以后json.loads(字符串/bytes格式)
import json
json.loads()
requests发送请求如何使用代理
ret=requests.get('http://101.133.225.166:8088/',
proxies={'http':'222.95.241.88:3000'}
)
print(ret.text)
django如何取出客户端的ip地址(request.META.get('REMOTE_ATTR'), 取出的是代理ip;真正的项目中,一般会使用代理池,自己是可以搭建代理池的,去爬取免费代理.
总结
'''
1、总结爬虫流程:
爬取--->解析--->存储
2、爬虫所需工具:
请求库:requests,selenium
解析库:正则,beautifulsoup,pyquery
存储库:文件,MySQL,Mongodb,Redis
3、爬虫常用框架:
scrapy
4、requests模块就是模拟http请求,请求有什么,requests模块就可以放什么,headers(user-agent,referer),cookies,data/json,
5、如果不做爬虫,requests模块可以单独使用,以后涉及到两个服务(两个项目)之间做数据交互
'''
并发爬取某视频网站
import requests
import re
import time
import hashlib
def get_page(url):
print('GET %s' %url)
try:
response=requests.get(url)
if response.status_code == 200:
return response.content
except Exception:
pass
def parse_index(res):
obj=re.compile('class="items.*?<a href="(.*?)"',re.S)
detail_urls=obj.findall(res.decode('gbk'))
for detail_url in detail_urls:
if not detail_url.startswith('http'):
detail_url='http://www.xiaohuar.com'+detail_url
yield detail_url
def parse_detail(res):
obj=re.compile('id="media".*?src="(.*?)"',re.S)
res=obj.findall(res.decode('gbk'))
if len(res) > 0:
movie_url=res[0]
return movie_url
def save(movie_url):
response=requests.get(movie_url,stream=False)
if response.status_code == 200:
m=hashlib.md5()
m.update(('%s%s.mp4' %(movie_url,time.time())).encode('utf-8'))
filename=m.hexdigest()
with open(r'./movies/%s.mp4' %filename,'wb') as f:
f.write(response.content)
f.flush()
def main():
index_url='http://www.xiaohuar.com/list-3-{0}.html'
for i in range(5):
print('*'*50,i)
#爬取主页面
index_page=get_page(index_url.format(i,))
#解析主页面,拿到视频所在的地址列表
detail_urls=parse_index(index_page)
#循环爬取视频页
for detail_url in detail_urls:
#爬取视频页
detail_page=get_page(detail_url)
#拿到视频的url
movie_url=parse_detail(detail_page)
if movie_url:
#保存视频
save(movie_url)
if __name__ == '__main__':
main()
#并发爬取
from concurrent.futures import ThreadPoolExecutor
import queue
import requests
import re
import time
import hashlib
from threading import current_thread
p=ThreadPoolExecutor(50)
def get_page(url):
print('%s GET %s' %(current_thread().getName(),url))
try:
response=requests.get(url)
if response.status_code == 200:
return response.content
except Exception as e:
print(e)
def parse_index(res):
print('%s parse index ' %current_thread().getName())
res=res.result()
obj=re.compile('class="items.*?<a href="(.*?)"',re.S)
detail_urls=obj.findall(res.decode('gbk'))
for detail_url in detail_urls:
if not detail_url.startswith('http'):
detail_url='http://www.xiaohuar.com'+detail_url
p.submit(get_page,detail_url).add_done_callback(parse_detail)
def parse_detail(res):
print('%s parse detail ' %current_thread().getName())
res=res.result()
obj=re.compile('id="media".*?src="(.*?)"',re.S)
res=obj.findall(res.decode('gbk'))
if len(res) > 0:
movie_url=res[0]
print('MOVIE_URL: ',movie_url)
with open('db.txt','a') as f:
f.write('%s\n' %movie_url)
# save(movie_url)
p.submit(save,movie_url)
print('%s下载任务已经提交' %movie_url)
def save(movie_url):
print('%s SAVE: %s' %(current_thread().getName(),movie_url))
try:
response=requests.get(movie_url,stream=False)
if response.status_code == 200:
m=hashlib.md5()
m.update(('%s%s.mp4' %(movie_url,time.time())).encode('utf-8'))
filename=m.hexdigest()
with open(r'./movies/%s.mp4' %filename,'wb') as f:
f.write(response.content)
f.flush()
except Exception as e:
print(e)
def main():
index_url='http://www.xiaohuar.com/list-3-{0}.html'
for i in range(5):
p.submit(get_page,index_url.format(i,)).add_done_callback(parse_index)
if __name__ == '__main__':
main()