爬虫——requests模块
一 爬虫简介
#1、什么是互联网? 互联网是由网络设备(网线,路由器,交换机,防火墙等等)和一台台计算机连接而成,像一张网一样。 #2、互联网建立的目的? 互联网的核心价值在于数据的共享/传递:数据是存放于一台台计算机上的,而将计算机互联到一起的目的就是为了能够方便彼此之间的数据共享/传递,
否则你只能拿U盘去别人的计算机上拷贝数据了。 #3、什么是上网?爬虫要做的是什么? 我们所谓的上网便是由用户端计算机发送请求给目标计算机,将目标计算机的数据下载到本地的过程。 #3.1 只不过,用户获取网络数据的方式是: 浏览器提交请求->下载网页代码->解析/渲染成页面。 #3.2 而爬虫程序要做的就是: 模拟浏览器发送请求->下载网页代码->只提取有用的数据->存放于数据库或文件中 #3.1与3.2的区别在于: 我们的爬虫程序只提取网页代码中对我们有用的数据 #4、总结爬虫 #4.1 爬虫的比喻: 如果我们把互联网比作一张大的蜘蛛网,那一台计算机上的数据便是蜘蛛网上的一个猎物,而爬虫程序就是一只小蜘蛛,沿着蜘蛛网抓取自己想要的猎物/数据 #4.2 爬虫的定义: 向网站发起请求,获取资源后分析并提取有用数据的程序 #4.3 爬虫的价值: 互联网中最有价值的便是数据,比如天猫商城的商品信息,链家网的租房信息,雪球网的证券投资信息等等,这些数据都代表了各个行业的真金白银,可以说,谁掌握了行业内的第一手数据,
谁就成了整个行业的主宰,如果把整个互联网的数据比喻为一座宝藏,那我们的爬虫课程就是来教大家如何来高效地挖掘这些宝藏,掌握了爬虫技能,你就成了所有互联网信息公司幕后的老板,
换言之,它们都在免费为你提供有价值的数据。
爬虫的流程:
#1、发起请求 使用http库向目标站点发起请求,即发送一个Request Request包含:请求头、请求体等 #2、获取响应内容 如果服务器能正常响应,则会得到一个Response Response包含:html,json,图片,视频等 #3、解析内容 解析html数据:正则表达式,第三方解析库如Beautifulsoup,pyquery等 解析json数据:json模块 解析二进制数据:以b的方式写入文件 #4、保存数据 数据库 文件
二 requests模块
关于requests模块爬虫的流程:
#1、总结爬虫流程: 爬取--->解析--->存储 #2、爬虫所需工具: 请求库:requests,selenium 解析库:正则,beautifulsoup,pyquery 存储库:文件,MySQL,Mongodb,Redis #3、爬虫常用框架: scrapy
requests请求的基本语法:
import requests requests.get("http://httpbin.org/get") requests.post("http://httpbin.org/post") requests.put("http://httpbin.org/put") requests.delete("http://httpbin.org/delete") requests.head("http://httpbin.org/get") requests.options("http://httpbin.org/get")
get请求:
1 基本请求 import requests response=requests.get('https://www.jd.com/',) with open("jd.html","wb") as f: f.write(response.content) 2 含参数请求 import requests response=requests.get('https://s.taobao.com/search?q=手机') response=requests.get('https://s.taobao.com/search',params={"q":"美女"}) 3 含请求头请求 import requests response=requests.get('https://dig.chouti.com/', headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36', } ) 4 含cookies请求 import uuid import requests url = 'http://httpbin.org/cookies' cookies = dict(sbid=str(uuid.uuid4())) res = requests.get(url, cookies=cookies) print(res.json()) 5 request.session() import requests # res=requests.get("https://www.zhihu.com/explore") # print(res.cookies.get_dict()) session=requests.session() res1=session.get("https://www.zhihu.com/explore") print(session.cookies.get_dict()) res2=session.get("https://www.zhihu.com/question/30565354/answer/463324517",cookies={"abs":"123"}
post请求:
1 data参数 requests.post()用法与requests.get()完全一致,特殊的是requests.post()多了一个data参数,用来存放请求体数据 response=requests.post("http://httpbin.org/post",params={"a":"10"}, data={"name":"yuan"}) 2 发送json数据 import requests<br> res1=requests.post(url='http://httpbin.org/post', data={'name':'yuan'}) #没有指定请求头,#默认的请求头:application/x-www-form-urlencoed print(res1.json()) res2=requests.post(url='http://httpbin.org/post',json={'age':"22",}) #默认的请求头:application/json) print(res2.json())
请求响应response:
response对象 (1) 常见属性 import requests respone=requests.get('https://sh.lianjia.com/ershoufang/') # respone属性 print(respone.text) # 得到的为text内容,为str类型的数据 print(respone.content) # 得到的为bytes类型 print(respone.status_code) # 请求响应状态码 print(respone.headers) # 响应头 print(respone.cookies) # 服务端返回的cookies
<RequestsCookieJar[<Cookie lianjia_ssid=67748407-774d-497c-b436-a2dea99025e2 for .lianjia.com/>, <Cookie lianjia_uuid=9a2e5006-07f2-4b3d-8745-406d7dea1acb for .lianjia.com/>, <Cookie select_city=310000 for .lianjia.com/>, <Cookie all-lj=762328e22710c88ff41f391dedabbc6f for sh.lianjia.com/>]>
print(respone.cookies.get_dict()) # 拿到cookies 中的数据以dict的形式。
{'lianjia_ssid': '67748407-774d-497c-b436-a2dea99025e2', 'lianjia_uuid': '9a2e5006-07f2-4b3d-8745-406d7dea1acb', 'select_city': '310000', 'all-lj': '762328e22710c88ff41f391dedabbc6f'}
print(respone.cookies.items(参数)) # 参数为get_dict中的key print(respone.url) # https://lianjia。。。。。 print(respone.history) # 会返回一个列表,列表中是跳转的url,历史跳转url,可以使用history来追踪重定向url print(respone.encoding) # utf8 (2) 编码问题 import requests response=requests.get('http://www.autohome.com/news') #response.encoding='gbk' #汽车之家网站返回的页面内容为gb2312编码的,而requests的默认编码为ISO-8859-1,如果不设置成gbk则中文乱码 with open("res.html","w") as f: f.write(response.text) (3) 下载二进制文件(图片,视频,音频) import requests response=requests.get('http://bangimg1.dahe.cn/forum/201612/10/200447p36yk96im76vatyk.jpg') with open("res.png","wb") as f: # f.write(response.content) # 比如下载视频时,如果视频100G,用response.content然后一下子写到文件中是不合理的 for line in response.iter_content(): f.write(line) (4) 解析json数据 import requests import json response=requests.get('http://httpbin.org/get') res1=json.loads(response.text) #太麻烦 res2=response.json() #直接获取json数据 print(res1==res2) (5) Redirection and History 默认情况下,除了 HEAD, Requests 会自动处理所有重定向。可以使用响应对象的 history 方法来追踪重定向。Response.history 是一个 Response 对象的列表,为了完成请求而创建了这些对象。这个对象列表按照从最老到最近的请求进行排序。 >>> r = requests.get('http://github.com') >>> r.url 'https://github.com/' >>> r.status_code 200 >>> r.history [<Response [301]>] 另外,还可以通过 allow_redirects 参数禁用重定向处理: >>> r = requests.get('http://github.com', allow_redirects=False) >>> r.status_code 301 >>> r.history []
爬虫实战:
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() 爬取校花网视频
import requests import re import json import time from concurrent.futures import ThreadPoolExecutor pool=ThreadPoolExecutor(50) def getPage(url): response=requests.get(url) return response.text def parsePage(res): com=re.compile('<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>' '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>',re.S) iter_result=com.finditer(res) return iter_result def gen_movie_info(iter_result): for i in iter_result: yield { "id":i.group("id"), "title":i.group("title"), "rating_num":i.group("rating_num"), "comment_num":i.group("comment_num"), } def stored(gen): with open("move_info.txt","a",encoding="utf8") as f: for line in gen: data=json.dumps(line,ensure_ascii=False) f.write(data+"\n") def spider_movie_info(url): res=getPage(url) iter_result=parsePage(res) gen=gen_movie_info(iter_result) stored(gen) def main(num): url='https://movie.douban.com/top250?start=%s&filter='%num pool.submit(spider_movie_info,url) #spider_movie_info(url) if __name__ == '__main__': before=time.time() count=0 for i in range(10): main(count) count+=25 after=time.time() print("总共耗费时间:",after-before)
import requests import re #请求1: r1=requests.get('https://github.com/login') r1_cookie=r1.cookies.get_dict() #拿到初始cookie(未被授权) authenticity_token=re.findall(r'name="authenticity_token".*?value="(.*?)"',r1.text)[0] #从页面中拿到CSRF TOKEN print("authenticity_token",authenticity_token) #第二次请求:带着初始cookie和TOKEN发送POST请求给登录页面,带上账号密码 data={ 'commit':'Sign in', 'utf8':'✓', 'authenticity_token':authenticity_token, 'login':'yuanchenqi0316@163.com', 'password':'yuanchenqi0316' } #请求2: r2=requests.post('https://github.com/session', data=data, cookies=r1_cookie, # allow_redirects=False ) print(r2.status_code) #200 print(r2.url) #看到的是跳转后的页面:https://github.com/ print(r2.history) #看到的是跳转前的response:[<Response [302]>] print(r2.history[0].text) #看到的是跳转前的response.text with open("result.html","wb") as f: f.write(r2.content)