爬虫是一个应用程序
是指某一个用于爬取数据的应用程序
爬取的目标可以是整个互联网,也可以是单独的某个服务器
本质上相当于cs结构中的client客户端程序
互联网中最宝贵的就是数据了,例如淘宝的商品数据,链家的房源信息,拉钩的招聘信息等等,这些数据就像一座矿山,爬虫就像是挖矿的工具,掌握了爬虫技术,你就成了矿山老板,各网站都在为你免费提供数据。爬虫通过网路获取模板服务器的数据,来为自己创造价值
首先明确 我们的爬虫是客户端 要爬的数据在服务器上储存 所以需要借助网络编程,底层的网络协议已经有现成的封装不需要自己写 数据传输已经解决,为了使我们的数据对方可以看懂,对方的数据我们可以看懂,所以绝大多数网络传输采取HTTP协议 HTTP协议是超文本传输协议 1.明确要爬取的url地址 2.发送请求 ①浏览器发送请求 ②第三方requests模块 ③内置urllib模块(不建议使用) ④selenium(自动化测试模块)用程序驱动浏览器发送请求 之所以用selenium是因为一些网站需要人机交互(点击,拖拽等等操作) ⑤针移动app可以使用代理服务器,可以截获app端发送的请求信息 Charles(青花瓷) 3.接收响应 ①浏览器接受响应后会渲染页面进行展示 无法拿到数据 所以更多情况下使用浏览器来分析请求详情 ②requests和urllib都会直接返回响应体 ③selenium 提供了 find_element... 的接口用于获取数据 4.解析数据 ①re ②BeautifulSoup 封装了常用的正则表达式 ③移动端返回的/ajax返回的json数据 直接json,load 5.储存数据 mysql等关系型数据库 MongoDB redis 等非关系型数据库 一般用在高并发爬虫中 Scrapy 爬虫框架
user-agent
refer
token
cookies
使用代理ip
请求地址 浏览器发送的请求URL地址 请求方法 get 参数跟在地址的后面 post 参数放在body中 请求头 请求头中的有用信息 - user-agent 用户代理,用来识别客户端类型 - refer 引用页面,用来识别用户从哪个页面过来的 - Cookie 当页面需要验证用户身份时使用 请求体 只在post请求时需要关注,通常post请求参数放在请求体中,例如登录时的用户名和密码 响应头 location 当请求被重定向时 就会带有该字段 可以通过状态码30* 来识别重定向 set-cookie:服务器返回的cookie信息,在访问一些隐私页面是需要带上cookie 响应体 服务器返回数据,可能一下几种类型 HTML格式静态页面 需要解析获取需要的数据 json格式的结构化数据 直接纯粹的数据 二进制数据(图片视频等) 通过文件操作直接写入文件
import requests resp=requests.get('http://www.jianshu.com') # respone属性 print(resp.text) # 获取解码后的内容 html print(resp.content) # 获取二进制字节数据,下载视频直接将其写入,如果是页面的话放入html文件直接可以打开该页面 print(resp.json()) # 对响应进行json反序列化 聚合数据网站可以拿到返回值为json格式数据进行测试 print(resp.status_code) #获取状态 20* 请求成功 30* 重定向 40* 客户端请求错误 50* 服务器内部错误 print(resp.headers) #查看响应头 print(resp.cookies) #获取返回的cookies信息 print(resp.cookies.get_dict()) #将返回的cookies信息以字典形式返回 print(resp.url) #请求地址 print(resp.request) #请求方式 print(resp.history) # 重定向历史 print(resp.encoding) # 响应体编码方式 print(resp.apparent_encoding) #文档编码方式 #当文档的编码方式与response解码方式不同时 需要手动指定 resp = requests.get("http://www.autohome.com/news") # 以文档声明的方式来解码 resp.encoding = resp.apparent_encoding print(resp.text) print(resp.raw.read(100)) #以流的方式读取原始数据,没有经过HTTP协议解析的数据,必须明确stream=True
#给服务器传参数
#get请求
params:给服务器传递参数,格式是一个字典 相当于将参数放在地址后面 requests.get('url',params={'key':'value'})
#post请求 data:post方法给服务器传递数据 data={'name':'wade','pwd':'123'} json:post方法给服务器传递数据 json={'name':'wade','pwd':'123'}
在通过requests.post()进行POST请求时,传入报文的参数有两个,一个是data,一个是json。
data与json既可以是str类型也可以是dict类型
区别:
1.不管json是str还是dict,如果不指定headers中的content-type,默认为application/json
2.data为dict时,如果不指定content-type,默认为application/x-www-form-urlencoded,相当于不同form表单提交的形式
3.data为str时,如果不指定content-type,默认为application/json
4.application/json格式提取数据时只可以用request.body取得数据,application/x-www-form-urlencoded提取数据时既可以用request.body也可以用request.POST
headers coookies #超时时间 第一个参数为连接超时时间,第二个为响应超时时间 timeout requests.post('http://www.baidu.com',timeout=(10,10)) allow_redirects : 是否允许重定向 proxies:使用代理服务器请求 ''' # 搜索免费http代理,写个爬虫扒http代理 import random # 代理池 ******** 对于爬虫而言是相当重要的一个参数 ps = ["121.228.240.101:9999","121.228.240.101:9999","121.228.240.101:9999"] #使用代理服务器请求 resp = requests.post("http://news.baidu.com/?tn=news",proxies={"HTTP":random.choice(ps)}) with open("wade.html","wb") as f: f.write(resp.content) print(resp.text) ''' files:上传文件 f = open(r"E:python-li爬虫aidu.html","rb") # 接收一个字典 key是服务器用于提取文件的字段名 f时要上传的文件对象 resp = requests.post("http://httpbin.org/post",files={"img":f}) print(resp.status_code)
进入梨视频页面 右键检查 点击Network 勾选Preserve log(保存日志,否则只显示每次响应的链接) 和 Disable cache(禁用缓存) 刷新页面 点击第一条请求 点击请求中的Headers 拿到Request URL 并取出其中的有用信息 点击Response 我们可以看到解码后的内容与respone.text内容一样 爬取的流程 1.爬取首页中所有的视频的id 2.拼接详情地址并请求 3.通过re解析数据 4.将结果存储为json数据 ''' import requests import re import json import os from concurrent.futures import ThreadPoolExecutor #要爬取的页数 page_num = int(input('请输入要爬取的页数:')) #爬取的分类ID category_id =int(input('请输入要爬取的分类ID:')) #储存解析完成的数据 datas = [] #根目录 dir = os.path.dirname(__file__) #请求首页列表 def get_page_data(categoryId): url = "https://www.pearvideo.com/category_loading.jsp?reqType=5&categoryId=%s&start="%categoryId for i in range(page_num): url1 = url + str(i*12) response = requests.get(url1) if response.status_code == 200: print('第%s页请求成功!'%(i+1)) get_details(response) print('第%s获取完毕!'%(i+1)) #获取详情 def get_details(response): res = re.findall('<a href="(video_d+)"',response.text) base_url = 'https://www.pearvideo.com/' for i in res: #拼接详情页面的地址 detail_url = base_url + i detail_resp = requests.get(detail_url) #解析标题 title = re.search('<h1 class="video-tt">(.*?)</h1>', detail_resp.text).group(1) #时间 sub_date = re.search('<div class="date">(.*?)</div>', detail_resp.text).group(1) #点赞数 f_count = re.search('<div class="fav" data-id="d+">(d+)</div>', detail_resp.text).group(1) #发布者姓名 author = re.search('</i>(.*?)</div>', detail_resp.text).group(1) #详请 content = re.search('<div class="summary">(.*?)</div>', detail_resp.text).group(1) #视频地址 video_url = re.search('srcUrl="(.*?)"',detail_resp.text).group(1) #获取的数据字典 dic = {'title':title,'sub_date':sub_date,'f_count':f_count,'author':author,'content':content,'video_url':video_url} #开始下载视频文件 #异步提交任务到线程池(IO密集型) download_video(video_url,title) pool.submit(download_video,video_url,title) datas.append(dic) #下载视频 def download_video(video_url,video_name): try: print('开始下载',video_name) response = requests.get(video_url) video_name = video_name.replace('"','') video_name = video_name.replace('?','') file_path = os.path.join(dir,'videos',video_name+'.mp4') #必须存在videos文件夹 if os.path.exists(file_path): print(video_name,'已经下载过了!') return with open(file_path,'wb') as f: f.write(response.content) print('下载完成!') except Exception as e: print('任务失败,%s'%e) #将数据写入json文件 def write_json(): with open('datas.josn','wt') as f: json.dump(datas,f) if __name__ == '__main__': #开启线程 pool = ThreadPoolExecutor() #获取详请 get_page_data(category_id) #写入 write_json() '''
import requests key = input('请输入关键字:') #百度搜索url https://www.baidu.com/s?wd=%E9%9F%A6%E5%BE%B7 url = 'https://www.baidu.com/s?' #Request Headers明确User-Agent user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' response = requests.get(url, #get请求 参数放在地址后或是使用params参数来指定 一个字典 params={'wd':key}, #请求头中携带 headers={'User-Agent':user_agent}) with open('baidu.html','wb') as f: f.write(response.content)
import requests url = "https://github.com/" user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" resp = requests.get(url, #登陆成功后,从Request Headers 中获取User-Agent 和 Cookie headers = {"user-agent":user_agent, "Cookie":"_ga=GA1.2.239001333.1554254017; _octo=GH1.1.152181730.1554254040; has_recent_activity=1; tz=Asia%2FShanghai; _device_id=6796ba7f23149d5bfaa3e56c429ff9e4; user_session=b99nc_Q_zIicwhtSgNiU0NlSzEoo6QSzaJ8YNThH1XEXbGwp; __Host-user_session_same_site=b99nc_Q_zIicwhtSgNiU0NlSzEoo6QSzaJ8YNThH1XEXbGwp; logged_in=yes; dotcom_user=lizeqian828; _gh_sess=UUY1OHlmK3BVR1FVZGk2OWlmN21KOUN3MUErYS83KzVjY0hGb1UwMCtJYldNbklTNVlHVFhPZ2NoMUxzRUhTM29vV0czV1djRUdMcjRRZEJzUzRYdW92M2xRVW9CY1NOak5wbGtBVFhFV1dxZmpmeVJld3JudEQvVFdON2RTTXNkM1pDcjlDSk5DNXZEYnNHUmovenE1d2FVZzlRSHdLeE9rWEk2TDU0ek4yRWpzeGNsd01OY2ZQZ0g3QmhYT1JwVzRvbDRJdXZFVnpGUzhxNCtLcUtQM0FQRDlWSmtENmwwdW96SEJTTkpHTmRWNFFoT1lBYzQvd1BPM21EOVVqRUZORUQwbHFxbitGZHFVaFJmVGNVK1E9PS0tZFJSdk5CVFRRcEp5RndwQzIranNpUT09--8df770bf594457931f66844d2f994068d7c8b587"}, ) #状态码 print(resp.status_code) #判断是否登陆成功 True成功 print("Your most active repositories will appear here" in resp.text)
post请求 参数使用data参数来指定 是一个字典 如果是登录请求一般都需要带token 先发送请求获取token的值 token值一定在一个form表单中 存在于页面数据及响应体中 """ 1.获取token 地址:https://github.com/login token放在了响应体中 """ import requests,re # 1.获取token #获取登录界面的地址get请求 login_page_url = "https://github.com/login" resp = requests.get(login_page_url) # 请求首页 token = re.search('authenticity_token" value="(.*?)" /> ',resp.text).group(1) # 获取返回的cookie,进入登录页面会产生一个cookie,必须获取否则不能提交post请求 cookie = resp.cookies.get_dict() # 2.请求登录接口 #提交完成登录的地址post请求 login_url = "https://github.com/session" resp2 = requests.post(login_url, headers={ "Referer": "https://github.com/login", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36", }, #post请求的数据以Form Data形式发给服务器 data={"commit": "Sign in", "utf8": "✓", "authenticity_token": token, "login": "lizeqian828", "password": "lizeqian0828", "webauthn-support": "supported"}, cookies = cookie) #状态码 print(resp2.status_code) with open("github_home.html","wb") as f: f.write(resp2.content) #判断是否登录成功 print("Your most active repositories will appear here" in resp2.text)
""" session 可以自动获取cookie并传递 """ import requests, re session = requests.session() # 1.获取token # 获取登录界面的地址get请求 login_page_url = "https://github.com/login" resp = session.get(login_page_url) # 请求首页 token = re.search('authenticity_token" value="(.*?)" /> ', resp.text).group(1) # 2.请求登录接口 # 提交完成登录的地址post请求 login_url = "https://github.com/session" resp2 = session.post(login_url, headers={ "Referer": "https://github.com/login", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36", }, # post请求的数据以Form Data形式发给服务器 data={"commit": "Sign in", "utf8": "✓", "authenticity_token": token, "login": "lizeqian828", "password": "lizeqian0828", "webauthn-support": "supported"}, #默认为True登陆成功完成跳转,False不完成跳转 allow_redirects=False) # 状态码 print(resp2.status_code) with open("github_home.html", "wb") as f: f.write(resp2.content) # 判断是否登录成功 print("Your most active repositories will appear here" in resp2.text)