摘要:并不是所有爬虫都遵守,一般只有大型搜索引擎爬虫才会遵守。的端口号为的端口号为工作原理网络爬虫抓取过程可以理解为模拟浏览器操作的过程。表示服务器成功接收请求并已完成整个处理过程。
爬虫概念
数据获取的方式:
企业生产的用户数据:大型互联网公司有海量用户,所以他们积累数据有天然优势。有数据意识的中小型企业,也开始积累的数据。
数据管理咨询公司
政府/机构提供的公开数据
第三方数据平台购买数据
爬虫爬取数据
什么是爬虫
抓去网页数据的程序
网页三大特征:
每个网页都有自己的URL
网页都使用HTML标记语言来描述页面信息
网页都使用HTTP/HTTPS协议来传输HTML数据
爬虫的设计思路
确定需要爬取的网页URL地址
通过HTTP/HTTPS协议来获取对应的HTML页面
提取HTML页面中的数据
如果是需要的数据,就保存起来
如果页面是其它URL,那就继续爬取
如何抓取HTML页面
HTTP协议请求的处理,urllib, urllib2, requests,处理后的请求可以模拟浏览器发送请求,获取服务器响应的文件
解析服务器响应的内容
re, xpath(常用), BeautifulSoup4(bs4), jsonpath, pyquery等使用某种描述性一样来给需要提取的数据定义一个匹配规则,符合这个规则的数据就会被匹配。
如何采集动态HTML,验证码的处理
Selenium(自动化测试工具) + PhantomJS(无界面浏览器)
验证码处理通过Tesseract: 机器图像识别系统(图片中的文本识别)
Scrapy框架
(Scrapy, Pyspider)
高性能高定制型(异步网络框架twisted),所以数据下载速度快
提供了数据存储,数据下载,提取规则等组件
分布式策略
是否有那么多的机器去做分布式?
获取的数据是否值得搭建分布式系统?
使用scrapy-redis来搭建,在Scrapy的基础上添加了一套 Redis数据库为核心的一套组件,让Scrapy框架支持分布式的功能。主要在Redis中做请求指纹去重,请求分配,数据临时存储
爬虫 - 反爬虫 - 反反爬虫
反爬虫: User-Agent, IP, 代理, 验证码, 动态数据加载, 加密数据
数据的价值,是否值得去费劲去做反爬虫,一般做到代理阶段或封IP。
机器成本 + 人力成本 > 数据价值
爬虫和反爬虫之间的斗争,最后一定是爬虫获胜。
只要是真实用户可以浏览的网页数据,爬虫就一定能爬下来。(爬虫模拟浏览器获取数据)
爬虫集合awesome-spider
通用爬虫
搜索引擎使用的爬虫系统
目标:尽可能把互联网上所有的网页下载下来,放到本地服务器里形成备份,再对这些网页做相关处理(提取关键字,去掉广告),最后提供一个用户检索接口
抓取流程:
首先选取一部分已有的URL,把这些URL放到待爬取队列。
从队列里去取出这些URL,然后解析DNS得到主机IP,然后去这个IP对应的服务器下载HTML页面,保存到搜索引擎的本地服务器里,之后把这个已经爬过的URL放入到已经爬取队列中
分析网页内容,找出网页中的其它URL内容,继续爬取。
搜索引擎如何获取一个新网站的URL:
主动向搜索引擎提交网址: 百度搜索资源平台
在其它网站设置外链
搜索引擎会和DNS服务商进行合作,可以快速收录新的网站
通用爬虫并不是万物皆可爬,它也需要遵守规则:
Robots协议,协议会指明通用爬虫可以爬取网页的权限。
Robots.txt并不是所有爬虫都遵守,一般只有大型搜索引擎爬虫才会遵守。
通用爬虫工作流程:
爬取网页 -> 存储数据 -> 内容处理 -> 提供检索/排名服务
搜索引擎排名:
PageRank值:根据网站的流量(pv),流量越高,排名约靠前
竞价排名
通用爬虫的缺点:
只能提供和文本相关的内容(HTML,Word,PDF)等,但是不能提供多媒体(音乐,视频,图片)和二进制文件。
提供的结果千篇一律,不能针对不同领域的人提供不同的搜索结果。
不能理解人类语义上的检索。
DNS: 把域名解析成IP
聚焦爬虫
爬虫程序员写的针对某种内容爬虫。(针对通用爬虫的缺点)
面向主题爬虫,面向需求爬虫,会针对某种特定的内容去爬取信息,而且会保证内容信息和需求尽可能相关。
HTTP&HTTPSHTTP协议(HyperText Transfer Protocol,超文本传输协议):是一种发布和接收HTML页面的方法。
HTTPS(Hypertext Transfer Protocol over Secure Socket Layer)简单讲是HTTP的安全版,在HTTP下加入SSL层。
SSL(Secure Sockets Layer 安全套接层)主要用于Web的安全传输协议,在传输层对网络连接进行加密,保障在Internet上数据传输的安全。
HTTP的端口号为80
HTTPS的端口号为443
HTTP工作原理
网络爬虫抓取过程可以理解为模拟浏览器操作的过程。
浏览器的主要功能是向服务器发出请求,在浏览器窗口中展示您选择的网络资源,HTTP是一套计算机通过网络进行通信的规则。
常用的请求报头:
Host (主机和端口号): 对应网址URL中的Web名称和端口号,用于指定被请求资源的Internet主机和端口号,通常属于URL的一部分。
Connection (链接类型): 表示客户端与服务连接类型
Client 发起一个包含 Connection:keep-alive 的请求,HTTP/1.1使用 keep-alive 为默认值。
Server收到请求后:如果 Server 支持 keep-alive,回复一个包含 Connection:keep-alive 的响应,不关闭连接; 如果 Server 不支持keep-alive,回复一个包含 Connection:close 的响应,关闭连接。
如果client收到包含 Connection:keep-alive 的响应,向同一个连接发送下一个请求,直到一方主动关闭连接。
keep-alive在很多情况下能够重用连接,减少资源消耗,缩短响应时间,比如当浏览器需要多个文件时(比如一个HTML文件和相关的图形文件),不需要每次都去请求建立连接。
Upgrade-Insecure-Requests (升级为HTTPS请求): 升级不安全的请求,意思是会在加载 http 资源时自动替换成 https 请求,让浏览器不再显示https页面中的http请求警报。(HTTPS 是以安全为目标的 HTTP 通道,所以在 HTTPS 承载的页面上不允许出现 HTTP 请求,一旦出现就是提示或报错。)
User-Agent (浏览器名称): 是客户浏览器的名称
Accept (传输文件类型): 指浏览器或其他客户端可以接受的MIME(Multipurpose Internet Mail Extensions(多用途互联网邮件扩展))文件类型,服务器可以根据它判断并返回适当的文件格式。
Accept: */*:表示什么都可以接收。
Accept:image/gif:表明客户端希望接受GIF图像格式的资源;
Accept:text/html:表明客户端希望接受html文本。
Accept: text/html, application/xhtml+xml;q=0.9, image/*;q=0.8:表示浏览器支持的 MIME 类型分别是 html文本、xhtml和xml文档、所有的图像格式资源。html中文件类型的accept属性有哪些
Referer (页面跳转处): 表明产生请求的网页来自于哪个URL,用户是从该 Referer页面访问到当前请求的页面。这个属性可以用来跟踪Web请求来自哪个页面,是从什么网站来的等。有时候遇到下载某网站图片,需要对应的referer,否则无法下载图片,那是因为人家做了防盗链,原理就是根据referer去判断是否是本网站的地址,如果不是,则拒绝,如果是,就可以下载;
Accept-Encoding(文件编解码格式): 指出浏览器可以接受的编码方式。编码方式不同于文件格式,它是为了压缩文件并加速文件传递速度。浏览器在接收到Web响应之后先解码,然后再检查文件格式,许多情形下这可以减少大量的下载时间。例如:Accept-Encoding:gzip;q=1.0, identity; q=0.5, *;q=0
Accept-Language(语言种类): 指出浏览器可以接受的语言种类,如en或en-us指英语,zh或者zh-cn指中文,当服务器能够提供一种以上的语言版本时要用到。
Accept-Charset(字符编码): 指出浏览器可以接受的字符编码。例如:Accept-Charset:iso-8859-1,gb2312,utf-8
Cookie (Cookie): 浏览器用这个属性向服务器发送Cookie。Cookie是在浏览器中寄存的小型数据体,它可以记载和服务器相关的用户信息,也可以用来实现会话功能
Content-Type (POST数据类型): POST请求里用来表示的内容类型。例如:Content-Type = Text/XML; charset=gb2312:
常用的响应报头(了解):
Cache-Control:must-revalidate, no-cache, private: 告诉客户端,服务端不希望客户端缓存资源,在下次请求资源时,必须要从新请求服务器,不能从缓存副本中获取资源。
Connection:keep-alive: 客户端服务器的tcp连接也是一个长连接,客户端可以继续使用这个tcp连接发送http请求
Content-Encoding:gzip: 告诉客户端,服务端发送的资源是采用gzip编码的,客户端看到这个信息后,应该采用gzip对资源进行解码。
Content-Type:text/html;charset=UTF-8: 告诉客户端,资源文件的类型,还有字符编码,客户端通过utf-8对资源进行解码,然后对资源进行html解析。
Date:Sun, 21 Sep 2016 06:18:21 GMT: 服务端发送资源时的服务器时间,GMT是格林尼治所在地的标准时间。http协议中发送的时间都是GMT的,这主要是解决在互联网上,不同时区在相互请求资源的时候,时间混乱问题。
Expires:Sun, 1 Jan 2000 01:00:00 GMT: 这个响应头也是跟缓存有关的,告诉客户端在这个时间前,可以直接访问缓存副本,很显然这个值会存在问题,因为客户端和服务器的时间不一定会都是相同的,如果时间不同就会导致问题。所以这个响应头是没有Cache-Control:max-age=*这个响应头准确的,因为max-age=date中的date是个相对时间.
Pragma:no-cache: 这个含义与Cache-Control等同。
Server:Tengine/1.4.6: 这个是服务器和相对应的版本,只是告诉客户端服务器的信息。
Transfer-Encoding:chunked: 这个响应头告诉客户端,服务器发送的资源的方式是分块发送的。
响应状态码:
100~199:表示服务器成功接收部分请求,要求客户端继续提交其余请求才能完成整个处理过程。
200~299:表示服务器成功接收请求并已完成整个处理过程。常用200(OK 请求成功)。
300~399:为完成请求,客户需进一步细化请求。例如:请求的资源已经移动一个新地址、常用302(所请求的页面已经临时转移至新的url)、307和304(使用缓存资源)。
400~499:客户端的请求有错误,常用404(服务器无法找到被请求的页面)、403(服务器拒绝访问,权限不够)。
500~599:服务器端出现错误,常用500(请求未完成。服务器遇到不可预知的情况)。
Cookie 和 Session:
因为服务器和客户端的交互仅限于请求/响应过程,结束之后便断开,在下一次请求时,服务器会认为新的客户端。为了维护他们之间的链接,让服务器知道这是前一个用户发送的请求,必须在一个地方保存客户端的信息
Cookie:通过在客户端 记录的信息确定用户的身份。
Session:通过在服务器端 记录的信息确定用户的身份。
urllib.request
linux中的py源码文件位置:
python自带:vim /usr/lib/python2.7/urllib2.py
pip安装:vim /usr/local/lib/python3.6/site-packages/django/http/cookie.py
urllib2.urlopen
# -*- coding:utf-8 -*- import urllib.request as urllib2 # 返回类文件对象 response = urllib2.urlopen("http://www.baidu.com/") # urlopen不支持构造 # 服务器返回类文件对象支持python文件对象的操作方法 # read()方法就是读取文件里面的全部内容,返回字符串 html = response.read() print(html)
Request
# -*- coding:utf-8 -*- import urllib.request as urllib2 ua_headres = { "User_Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Mobile Safari/537.36" } # urllib2.Request(url, data, headres) # 通过urllib2.Request()方法构造一个请求对象 requset = urllib2.Request("http://www.baidu.com", headers=ua_headres) # 返回类文件对象, urlopen不支持构造 response = urllib2.urlopen(requset) # 服务器返回类文件对象支持python文件对象的操作方法 # read()方法就是读取文件里面的全部内容,返回字符串 html = response.read() print(html)
User_Agent,是发送请求必须带的请求头
Response响应
response是服务器响应的类文件,除了支持文件操作的方法外,常用的方法也有:
respnse.getcode(), response.geturl(), response.info()
#condig=utf-8 import urllib.request as urllib2 # print(dir(urllib2)) ua_headres = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36 Core/1.53.4620.400 QQBrowser/9.7.13014.400" } request = urllib2.Request("http://www.baidu.com/", headers=ua_headres) response = urllib2.urlopen(request) html = response.read() # 返回HTTP的响应码,成功返回200 # 4 服务器页面出错, 5 服务器问题 print(response.getcode()) # 返回实际数据的url,防止重定向问题 print(response.geturl()) # 返回服务器响应报头信息 print(response.info()) # print(dir(response))
User-Agent历史
如果用一个合法的身份去请求别人网站,就是欢迎的,所以就应该给这个代码加上一个身份,就是所谓的User-Agent头。
urllib2默认的User-Agent头为:Python-urllib/x.y(x和y是Python主版本和次版本号,例如 Python-urllib/2.7
Mosaic世界上第一个浏览器:美国国家计算机应用中心
Netscape,网景:Netscape(支持框架)
Microsoft微软:Internet Explorer
第一次浏览器大战:网景公司失败
Mozilla 基金组织:Firefox 火狐 内核(Gecko内核)(浏览器支持内核开始,User-Agent开始逐渐使用)
User-Agent 决定用户的浏览器,为了获取更好的HTML页面效果
IE就给自己披着了个Mozilla的外皮
内核:
Mozilla: Firefox (Gecko)
IE: Trident
Opera: Presto
Linux: KHTML (like Gecko)
Apple: Webkit (like KTML)
Google: Chrome (like webkit)
add_header() & get_header()
add_header(): 添加/修改 一个HTTP报头
get_header(): 获取一个已有的HTTP报头值,只能第一个字母大写,其它的必须小写
# -*- coding:utf-8 -*- import urllib.request as urllib2 import random url = "http://www.baidu.com/" # 可以是User-Agent列表,也可以是代理列表。 作用:反反爬虫 ua_list = [ "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50" ] # 在User-Agent列表里随机选择一个User-Agent user_agent = random.choice(ua_list) # 构造一个请求 request = urllib2.Request(url) # add_header()方法,添加/修改 一个HTTP报头 request.add_header("User-Agent", user_agent) # get_header() 获取一个已有的HTTP报头值,只能第一个字母大写,其它的必须小写 request.get_header("User-agent") response = urllib2.urlopen(request) html = response.read() print(html)
urllib.urlencode
编码:
urlencode位置:urllib.parse.urlencode(values)。 其中values所需要编码的数据,参数只能为字典
解码:
unquote: urllib.parse.unquote(values)
#conding=utf-8 import urllib.parse test = { "test": "我的" } # 通过urllib.urlencode()方法,将字典键值对按URL编码转换,从而能被web服务器接受。 enCodeTest = urllib.parse.urlencode(test) # 冒号解析为等号 print(enCodeTest) # test=%E6%88%91%E7%9A%84 # 通过urllib.unquote()方法,把 URL编码字符串,转换回原先字符串。 print(urllib.parse.unquote(enCodeTest)) # test=我的
爬取百度贴吧起始页到结束页的案例
#conding=utf-8 import urllib.request import urllib.parse def loadPage(url, filename): """ 作用: 根据url发送请求,获取服务器响应文件 url: 需要爬取的url地址 filename: 处理的文件名 """ print("正在下载" + filename) headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36" } request = urllib.request.Request(url, headers=headers) return urllib.request.urlopen(request).read() def writePage(html, filenmae): """ 作用:将html内容写入到本地 html: 服务器响应文件内容 """ print("正在保存" + filenmae) # 文件写入 with open(filenmae, "w") as f: # with 之后,不需要做文件关闭还有其它上下文处理的操作 等同于 open(), write(), close() f.write(html.decode("utf-8")) print("-" * 30) print("thanks") def tiebaSpider(url, beginPage, endPage): """ 作用: 贴吧爬虫调度器,负责组合处理 url: 贴吧url的前部分 beginPage: 起始页 endPage: 结束页 """ for page in range(beginPage, endPage+1): pn = (page - 1) * 50 filename = "第" + str(page) + "页.html" fullurl = url + "&pn=" + str(pn) # print(fullurl) html = loadPage(fullurl, filename) writePage(html, filename) # print(html) if __name__ == "__main__": kw = input("请输入需要爬取的贴吧名:") beginPage = int(input("请输入起始页:")) endPage = int(input("请输入结束页:")) # https://tieba.baidu.com/f?ie=utf-8&kw=javascirpt&fr=search url = "https://tieba.baidu.com/f?" key = urllib.parse.urlencode({"kw": kw}) fullurl = url + key tiebaSpider(fullurl, beginPage, endPage)
POST请求的模拟
Get和Post请求的区别:
Get请求:查询参数在QueryString里保存
Post请求:查询参数在FormData中保存
Post请求:
# -*- coding:utf-8 -*- import urllib.request import urllib.parse url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule" key = input("请输入查询翻译的文字:") # 发送到服务器的表单数据,如果是中文需要转码 fromdata = { "i": key, "from": "AUTO", "to": "AUTO", "smartresult": "dict", "client": "fanyideskweb", "salt": "1528127663128", "sign": "c060b56b628f82259225f751c12da59a", "doctype": "json", "version": "2.1", "keyfrom": "fanyi.web", "action": "FY_BY_REALTIME", "typoResult": "false" } data = urllib.parse.urlencode(fromdata).encode("utf-8") headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36" } request = urllib.request.Request(url, data=data, headers=headers) html = urllib.request.urlopen(request).read().decode() print(html)
获取AJAX加载的内容
AJAX一般返回的是JSON,直接对AJAX地址进行post或get,就返回JSON数据了。
“作为一名爬虫工程师,最需要关注的是数据的来源”
# -*- coding:utf-8 -*- import urllib.request import urllib.parse url = "https://movie.douban.com/j/search_subjects?" headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36" } formdata = { "type": "movie", "tag": "热门", "sort": "recommend", "page_limit": 20, "page_start": 40 } data = urllib.parse.urlencode(formdata).encode("utf-8") request = urllib.request.Request(url, data=data, headers=headers) html = urllib.request.urlopen(request).read().decode() print(html)
处理HTTPS请求 SSL证书验证
网站的SSL证书是经过CA认证的,则能够正常访问
多带带处理SSL证书,让程序忽略SSL证书验证错误
# -*- coding:utf-8 -*- import urllib.request import ssl # 表示忽略未经核实的SSL证书认证 context = ssl._create_unverified_context() url = "https://www.12306.cn/mormhweb/" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36" } request = urllib.request.Request(url, headers = headers) # 在urlopen()方法里 指明添加 context 参数 response = urllib.request.urlopen(request, context = context) print(response.read())
CA: 数字证书认证中心的简称,是指发放、管理、废除数字证书的受信任的第三方机构(类似与身份证)
CA的作用: 检查证书持有者身份的合法性,并签发证书,以防证书被伪造或篡改,以及对证书和密钥进行管理
一般正常的网站都会主动出示自己的数字证书,来确保客户端和网站服务器之间的通信数据是加密安全的.
Handler和Opener的使用
自定义Handler
# -*- coding:utf-8 -*- import urllib.request # 构建一个HTTPHandler处理器对象,支持处理HTTP的请求 # http_hander = urllib.request.HTTPHandler() http_hander = urllib.request.HTTPHandler(debuglevel=1) # 调用build_opener()方法构建一个自定义的opener对象,参数是构建的处理器对象 opener = urllib.request.build_opener(http_hander) req = urllib.request.Request("http://www.baidu.com") res = opener.open(req) print(res.read().decode())
开放代理和私密代理
高匿:无法拿到真正的物理ip,只能获取代理服务器ip
透明:能看到代理服务器ip,也可以看到物理ip地址
快代理
西刺免费代理
使用代理IP,这是爬虫/反爬虫的第二大招,通常也是最好用的。
很多网站会检测某一段时间某个IP的访问次数(通过流量统计,系统日志等),如果访问次数多的不像正常人,它会禁止这个IP的访问。
可以设置一些代理服务器,每隔一段时间换一个代理,就算IP被禁止,依然可以换个IP继续爬取。
# -*- coding:utf-8 -*- import urllib.request # 代理开关,是否启用代理 proxyswitch = True # 公开代理 proxy_ip = { "http": "123.57.217.208:3128" } # 私密代理 授权的账号密码 # proxy_ip_auth = { # "http": "user:passwd@ip:prot" # } # 构建一个handler对象,参数是一个字典类型,包括代理类型和代理服务器ip+prot http_proxy_handler = urllib.request.ProxyHandler(proxy_ip) # 构建一个没有代理对象的处理器对象 null_proxy_headler = urllib.request.ProxyHandler({}) if proxyswitch: opener = urllib.request.build_opener(http_proxy_handler) else: opener = urllib.request.build_opener(null_proxy_headler) # 构建一个全局的opener,之后的所有请求都可以用urlopen()方式发送,也附带Handler功能 urllib.request.install_opener(opener) request = urllib.request.Request("http://www.baidu.com/") response = urllib.request.urlopen(request) # response = opener.open(request) print(response.read().decode())
ProxyBasicAuthHandler(代理授权验证):
#conding=utf-8 import urllib.request user = "" passwd = "" proxyserver = "" # 构建一个密码管理对象,用来保存需要处理的用户名和密码 passwdmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() # 添加账户信息,第一个参数realm是与远程服务器相关的域信息,一般都是写None,后面三个参数分别是 代理服务器、用户名、密码 passwdmgr.add_password(None, proxyserver, user, passwd) # 构建一个代理基础用户名/密码验证的ProxyBasicAuthHandler处理器对象,参数是创建的密码管理对象 proxy_auth_handler = urllib.request.ProxyDigestAuthHandler(passwdmgr) # 通过 build_opener()方法使用这些代理Handler对象,创建自定义opener对象,参数包括构建的 proxy_handler 和 proxyauth_handler opener = urllib.request.build_opener(proxy_auth_handler) request = urllib.request.Request("http://www.baidu.com/") response = opener.open(request) print(response.read().decode())Cookie
Cookie 是指某些网站服务器为了辨别用户身份和进行Session跟踪,而储存在用户浏览器上的文本文件,Cookie可以保持登录信息到用户下次与服务器的会话。
HTTP是无状态的面向连接的协议, 为了保持连接状态, 引入了Cookie机制 Cookie是http消息头中的一种属性
Cookie名字(Name) Cookie的值(Value) Cookie的过期时间(Expires/Max-Age) Cookie作用路径(Path) Cookie所在域名(Domain), 使用Cookie进行安全连接(Secure)。 前两个参数是Cookie应用的必要条件,另外,还包括Cookie大小(Size,不同浏览器对Cookie个数及大小限制是有差异的)。
Cookie由变量名和值组成,根据 Netscape公司的规定,Cookie格式如下:
Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
在Python处理Cookie,一般是通过cookielib模块和urllib2模块的HTTPCookieProcessor处理器类一起使用。
cookielib模块:主要作用是提供用于存储cookie的对象
HTTPCookieProcessor处理器:主要作用是处理这些cookie对象,并构建handler对象。
cookielib 库
该模块主要的对象有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar
一般情况下,只用CookieJar(),如果需要和本地文件交互,就需要使用MozillaCookjar()和LWPCookieJar()
获取Cookie,并保存到CookieJar()对象中:
#!/usr/local/bin/python import urllib.request import http.cookiejar cookiejar = http.cookiejar.CookieJar() http_handler = urllib.request.HTTPCookieProcessor(cookiejar) opener = urllib.request.build_opener(http_handler) opener.open("http://www.baidu.com") cook_str = "" for item in cookiejar: cook_str = cook_str + item.name + "=" + item.value + ";" print(cook_str[:-1]) # BAIDUID=5DB0FC0C0DC9692BB8EE6EDC93A2EDEA:FG=1;BIDUPSID=5DB0FC0C0DC9692BB8EE6EDC93A2EDEA;H_PS_PSSID=1468_26259_21099_26350_26580;PSTM=1528615563;BDSVRTM=0;BD_HOME=0
访问网站获得cookie,并把获得的cookie保存在cookie文件中:
#!/usr/local/bin/python import http.cookiejar import urllib.request filename = "cookie.txt" # 声明一个MozillaCookieJar(有save实现)对象实例来保存cookie,之后写入文件 cookiejar = http.cookiejar.MozillaCookieJar(filename) handler = urllib.request.HTTPCookieProcessor(cookiejar) opener = urllib.request.build_opener(handler) req = opener.open("http://www.baidu.com") # 保存cookie到本地文件 cookiejar.save() print(1)非结构化数据和结构化数据
实际上爬虫一共就四个主要步骤:
明确目标 (要知道准备在哪个范围或者网站去搜索)
爬 (将所有的网站的内容全部爬下来)
取 (去掉对没用处的数据)
处理数据(按照想要的方式存储和使用)
re模块
pattern = re.compile(regExp) pattern.match(): 从起始位置开始查找,返回第一个符合规则的,只匹配一次。 pattern.search(): 从任意位置开始查找,返回第一个符合规则的,只匹配一次。 pattern.findall(): 所有的全部匹配,返回列表 pattern.split(): 分割字符串,返回列表 pattern.sub(): 替换
rs.I 忽略大小写 re.S 全文匹配
match(str, begin, end):
>>> pattern = re.compile(r"([a-z]+) ([a-z]+)", re.I) >>> m = pattern.match("hello world hello python") >>> m.group() "hello world" >>> m.group(1) "hello" >>> m.group(2) "world"
findall(str, begin, end):
>>> pattern = re.compile(r"d+") >>> pattern.findall("hello world 123 456 789") ["123", "456", "789"] >>>
split(str, count):
>>> pattern = re.compile(r"[sd;]+") >>> pattern.split("a ba;m; a ") ["a", "bx07", "m", "a", ""] >>> >>> pattern = re.compile("[sd;]+") >>> pattern.split(r"a ba;m; a ") ["a", "ba", "m", "a", ""] >>>
sub():
>>> pattern = re.compile(r"(w+)(w+)") >>> strs = "hello 123, world 456" >>> pattern.sub("hello world", strs) "hello world hello world, hello world hello world" >>>xpath
chrome插件:XPath Helper
XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在XML文档中对元素和属性进行遍历。
lxml库:
lxml是 一个HTML/XML的解析器,主要的功能是如何解析和提取HTML/XML数据。
获取属性:@src, @title, @class
获取内容: /text()
模糊查询: contains(@id, "模糊字符串")
xpath匹配规则:
//div[@class="pic imgcover"]/img/@src
xpath模糊匹配:
//div[contains(@id, "qiushi_tag")]
获取某个网站的图片:
#conding=utf-8 import urllib.request import urllib.parse from lxml import etree # 我乃河北,姓氏颜良 class getQdailyImg: def __init__(self, url): self.url = url def loadPage(self): print("正在下载...") headres = { "User-Agent": "ie 的user-Agent" } req = urllib.request.Request(self.url, headers=headres) html = urllib.request.urlopen(req).read().decode() xmlDom = etree.HTML(html) linkList = xmlDom.xpath("//div[@class="pic imgcover"]/img/@src") print(linkList) self.writePage(linkList) def writePage(self, data): for item in data: with open("img.txt", "w") as f: f.write(item) if __name__ == "__main__": qdI = getQdailyImg("http://www.qdaily.com/") qdI.loadPage()
# -*- coding:utf-8 -*- import urllib.request import json from lxml import etree url = "http://www.qiushibaike.com/8hr/page/1" headers = { "user-agent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB7.0)" } req = urllib.request.Request(url, headers=headers) html = urllib.request.urlopen(req).read() text = etree.HTML(html) # 作为根目录节点 node_list = text.xpath("//div[contains(@id, "qiushi_tag")]") items = {} for node in node_list: username = node.xpath("./div[@class="author clearfix"]//h2/text()")[0] image = node.xpath(".//div[@class="thumb"]//@src") content = node.xpath(".//div[@class="content"]/span")[0].text zan = node.xpath(".//i")[0].text comment = node.xpath(".//i")[1].text items = { "username": username, "image": image, "content": content, "zan": zan, "comment": comment } with open("qiushi.json", "a") as f: f.write(json.dumps(items, ensure_ascii=False) + " ") print("ok")BeautifulSoup
Beautiful Soup也是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML数据。
BeautifulSoup用来解析HTML比较简单,API非常人性化,支持CSS选择器、Python标准库中的HTML解析器,也支持lxml的 XML解析器。
pip install bs4
beautifulsoup4文档
tag: BeautifulSoup(html).div
attrs: BeautifulSoup(html).div.name,BeautifulSoup(html).div.attres
content: BeautifulSoup(html).span.string
#conding=utf-8 from bs4 import BeautifulSoup import requests import time def captchaMethod(captcha_data): with open("captcha.jpg", "wb") as f: f.write(captcha_data) return input("请输入验证码:") def getLoginZhihu(): # 构建Session对象,保存cookie值 sess = requests.Session() headers = { "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 QQBrowser/4.3.4986.400" } html = sess.post("https://www.zhihu.com/#sigin", headers=headers).text bs = BeautifulSoup(html, "lxml") _xsrf = bs.find("input", attrs={"name": "_xsrf"}).get("value") captcha_url = "https://www.zhihu.com/captcha.gif?r=%d&type=login" % (time.time() * 1000) captcha = sess.get(captcha_url, headers=headers).content # 获取验证码文字 text = captchaMethod(captcha) data = { "_xsrf": _xsrf, "email": "123636374@qq.com", "password": "ALARMCHIME", "captcha": text } # 登录 获取cookie res = sess.post("https://www.zhihu.com/login/email", data=data, headers=headers).text res = sess.get("https://www.zhihu.com/people/", headers) if __name__ == "__main__": getLoginZhihu()JSON和JSONPATH
Json和JsonPath应用
json.loads(): 把Json格式字符串解码转换成Python对象
json.dumps(): 实现python类型转化为json字符串,返回一个str对象 把一个Python对象编码转换成Json字符串
json.dump(): 将Python内置类型序列化为json对象后写入文件
json.load(): 读取文件中json形式的字符串元素 转化成python类型
dictStr = {"city": "北京", "name": "大猫"} print(json.dumps(dictStr, ensure_ascii=False)) # {"city": "北京", "name": "大刘"} listStr = [{"city": "北京"}, {"name": "大刘"}] json.dump(listStr, open("listStr.json","w"), ensure_ascii=False) strDict = json.load(open("dictStr.json")) print(strDict) # {u"city": u"u5317u4eac", u"name": u"u5927u5218"}
# -*- coding:utf-8 -*- import json import urllib.request import jsonpath headers = { "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 QQBrowser/4.3.4986.400" } url = "https://www.lagou.com/lbs/getAllCitySearchLabels.json" request = urllib.request.Request(url, headers=headers) response = urllib.request.urlopen(request) html = response.read().decode() unicodeStr = json.loads(html) content = jsonpath.jsonpath(unicodeStr, "$..name") print(content) array = json.dumps(content, ensure_ascii=False) with open("lagoucontent.json", "w") as f: f.write(array)多线程爬虫
一个进程可能包括多个线程,线程之间执行任务,必须通过加锁方式控制它们(阻塞)
父线程和子线程都关系,只要父线程执行完,不管子线程如何,都一并结束
计算机的核心是CPU,CPU承担了所有的计算任务
一个CPU核心一次只能执行一个任务
多个CPU核心同时可以执行多个任务
一个CPU一次只能执行一个进程,其它进程处于非运行
进程里包含的执行单元叫线程
一个进程 可以包含 多个线程
一个进程的内存空间是共享的,每个进程里的线程都可以使用这个共享空间
一个线程在使用这个共享空间的时候,其它线程必须等待它结束
通过“锁”实现,作用就是防止多个线程使用当前内存空间。
先使用的线程会加锁,锁上该空间,其它线程就在等待。
进程:表示程序的一次执行
线程:CPU运算的基本调度单位
GIL: Python里的执行通行证,而且只有唯一个。拿到通行证的线程才会执行
Python 的多线程适用于:大量密集的I/O处理 (多带带都任务,一个进程,只能执行一个任务)
Python 的多进程适用于:大量的密集并行计算
#conding=utf-8 import json import threading from queue import Queue import requests from lxml import etree CREAWL_EXIT = False PARSE_EXIT = False """ 采集线程 """ class ThreadCrawl(threading.Thread): def __init__(self, threadName, pageQueue, dataQueue): # threading.Thread.__init__(self) super(ThreadCrawl, self).__init__() # 多个父类,多重继承 self.threadName = threadName self.pageQueue = pageQueue self.dataQueue = dataQueue self.headers = { "user-agent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB7.0)" } def run(self): print("start" + self.threadName) while not CREAWL_EXIT: try: page = self.pageQueue.get(False) url = "https://www.qiushibaike.com/8hr/page/" + str(page) + "/" res = requests.get(url, headers=self.headers).text self.dataQueue.put(res) except: pass print("end" + self.threadName) """ 解析线程 """ class ThreadParse(threading.Thread): def __init__(self, threadingName, dataQueue, filename): super(ThreadParse, self).__init__() self.threadingName = threadingName self.dataQueue = dataQueue self.filename = filename def run(self): print("start" + self.threadingName) while not PARSE_EXIT: try: html = self.dataQueue.get(False) self.parse(html) except: pass print("end" + self.threadingName) def parse(self, html): text = etree.HTML(html) node_list = text.xpath("//div[contains(@id, "qiushi_tag")]") items = {} for node in node_list: username = node.xpath("./div[@class="author clearfix"]//h2/text()")[0] image = node.xpath(".//div[@class="thumb"]//@src") content = node.xpath(".//div[@class="content"]/span")[0].text zan = node.xpath(".//i")[0].text comment = node.xpath(".//i")[1].text items = { "username": username, "image": image, "content": content, "zan": zan, "comment": comment } self.filename.write(json.dumps(items, ensure_ascii=False) + " ") def main(): # 页码 pageQueue = Queue(10) # 放入1~10的数字 for i in range(1, 10+1): pageQueue.put(i) # 采集结果(每页的HTML源码) dataQueue = Queue() filename = open("duanzi.json", "a") crawlList = ["采集线程1", "采集线程2", "采集线程3"] threadcrawl = [] for threadName in crawlList: thread = ThreadCrawl(threadName, pageQueue, dataQueue) thread.start() threadcrawl.append(thread) parseList = ["解析线程1", "解析线程2", "解析线程3"] threadparse = [] for threadName in parseList: thread = ThreadParse(threadName, dataQueue, filename) thread.start() threadparse.append(thread) # 等待pageQueue队列为空, 或者 数据队列为空,也就是等待之前执行的操作执行完毕 while not pageQueue.empty() or not dataQueue.empty(): pass global CREAWL_EXIT CREAWL_EXIT = True print("queue队列为空") global PARSE_EXIT PARSE_EXIT = True print("data队列为空") for threadItem in crawlList: threadItem.join("") print("1") if __name__ == "__main__": main()
自动化测试unittest模块使用和模拟用户点击抓取数据(拿去ajax分页数据)
# -*- coding:utf-8 -*- import unittest from selenium import webdriver from bs4 import BeautifulSoup as bs class Douyu(unittest.TestCase): def setUp(self): self.driver = webdriver.PhantomJS() # unittest测试方法必须有`test`字样开头 def testDouyu(self): self.driver.get("https://www.douyu.com/directory/all") while True: soup = bs(self.driver.page_source, "lxml") names = soup.find_all("h3", {"class": "ellipsis"}) viewNums = soup.find_all("span", {"class": "dy-num fr"}) for name, viewNum in zip(names, viewNums): print("房间名" + name.get_text() + "; " + "观众人数" + viewNum.get_text()) # 在页面源码中找到"下一页"未隐藏的标签,就退出循环 if self.driver.page_source.find("shark-pager-disable-next") != -1: break # 一直点击下一页 self.driver.find_element_by_class_name("shark-pager-next").click() # 测试结束执行的方法 def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main()
执行javascript语句:execute_script
#conding=utf-8 from selenium import webdriver import time driver = webdriver.PhantomJS("/Users/linxingzhang/Library/Python/3.6/lib/python/site-packages/selenium/webdriver/phantomjs") driver.get("https://movie.douban.com/typerank?type_name=剧情&type=11&interval_id=100:90&action=") time.sleep(3) # 向下滚动10000像素 js = "document.body.scrollTop=10000" # js="var q=document.documentElement.scrollTop=10000" # 查看页面快照 driver.save_screenshot("douban.png") # 执行JS语句 driver.execute_script(js) time.sleep(10) # 查看页面快照 driver.save_screenshot("newdouban.png") driver.quit()
投票
import datetime import sys import threading import time from random import choice # choice() 方法返回一个列表,元组或字符串的随机项 import requests from lxml import etree from fake_useragent import UserAgent # 引入随机的UA # 设置user-agent列表,每次请求时,随机挑选一个user-agent ua_list = UserAgent() def get_ip(): """ 获取代理ip """ url = "http://www.xicidaili.com/nn" headers = { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "Host": "www.xicidaili.com", "Referer": "http: // www.xicidaili.com/nn", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6726.400 QQBrowser/10.2.2265.400" } ret = requests.get(url, headers=headers) xmlDom = etree.HTML(ret.text) data = xmlDom.xpath("//table[@id="ip_list"]//tr") z = [] for tr in data: if tr.xpath("td"): ip = tr.xpath("td")[1].text # 获取所有IP port = tr.xpath("td")[2].text # 获取所有端口 z.append(ip + ":" + port) return z def get_url(url, code=0, ips=[]): """ 投票 如果因为代理IP已失效造成投票失败,则会自动换一个代理IP后继续投票 """ try: ip = choice(ips) print(ip, "ip" * 5) except: return False else: proxies = { "http": ip } headers = { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "Host": "best.zhaopin.com", "Origin": "https: // best.zhaopin.com", "Referer": "https//best.zhaopin.com/?sid=121128100&site=sou", "User-Agent": ua_list.random } print(ua_list.random, "ua_list" * 5) try: data = {"bestid": "3713", "score": "5,5,5,5,5,5", "source": "best"} # 跳过证书的验证 verify=False result = requests.post(url, data=data, headers=headers, proxies=proxies) print(result, "result" * 5) except requests.exceptions.ConnectionError: print("ConnectionError") if not ips: print("ip 失效") sys.exit() # 删除不可用的代理IP if ip in ips: ips.remove(ip) # 重新请求url get_url(url, code=0, ips=[]) else: date = datetime.datetime.now().strftime("%H:%M:%S") # result.text() 投票成功显示1 失败显示0 print("第%s次 [%s] [%s]:投票%s (剩余可用代理IP数:%s)" % (code, date, ip, result.text, len(ips))) def main(): url = "https://best.zhaopin.com/API/ScoreCompany.ashx" # 投票的请求 ips = [] for i in range(6000): if i % 1000 == 0: ips.extend(get_ip()) # print("-" * 100) # print(ips) t = threading.Thread(target=get_url, args=(url, i, ips)) t.start() time.sleep(1) if __name__ == "__main__": main()Tesseract
机器识别中的文字识别
pip install pytesseract
识别图片中的文字:
#conding=utf-8 import pytesseract from PIL import Image image = Image.open("./mayday.jpg") text = pytesseract.image_to_string(image) print(text)asyncio & aiohttp
通过异步库aiohttp,asyncio爬取图片
# -*- coding:utf-8 -*- import asyncio import os import time import aiohttp import requests class Spider(object): def __init__(self): self.num = 1 if "load-img" not in os.listdir("."): os.mkdir("load-img") self.path = os.path.join(os.path.abspath("."), "load-img") os.chdir(self.path) # 进入文件下载路径 def run(self): start = time.time() for x in range(1, 101): # 爬取100张图片,更改数值,爬取更多图片 links = self.__get_img_links(x) tasks = [asyncio.ensure_future(self.__download_img( (link["id"], link["links"]["download"]) )) for link in links] loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks)) # if self.num >= 10: # break end = time.time() print("run %s s" % (end - start)) def __get_img_links(self, page): url = "https://unsplash.com/napi/photos" data = { "page": page, "per_page": 12, "order_by": "latest" } response = requests.get(url, params=data) if response.status_code == 200: return response.json() else: print("request %s" % response.status_code) async def __download_img(self, img): content = await self.__get_content(img[1]) with open(img[0] + ".jpg", "wb") as f: f.write(content) print("load %s page success" % self.num) self.num += 1 async def __get_content(self, link): async with aiohttp.ClientSession() as session: response = await session.get(link) content = await response.read() return content def main(): spider = Spider() spider.run() if __name__ == "__main__": main()
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/41772.html
摘要:以下这些项目,你拿来学习学习练练手。当你每个步骤都能做到很优秀的时候,你应该考虑如何组合这四个步骤,使你的爬虫达到效率最高,也就是所谓的爬虫策略问题,爬虫策略学习不是一朝一夕的事情,建议多看看一些比较优秀的爬虫的设计方案,比如说。 (一)如何学习Python 学习Python大致可以分为以下几个阶段: 1.刚上手的时候肯定是先过一遍Python最基本的知识,比如说:变量、数据结构、语法...
摘要:且本小白也亲身经历了整个从小白到爬虫初入门的过程,因此就斗胆在上开一个栏目,以我的图片爬虫全实现过程为例,以期用更简单清晰详尽的方式来帮助更多小白应对更大多数的爬虫实际问题。 前言: 一个月前,博主在学过python(一年前)、会一点网络(能按F12)的情况下,凭着热血和兴趣,开始了pyth...
摘要:这篇文章的题目有点大,但这并不是说我自觉对爬虫这块有多大见解,我只不过是想将自己的一些经验付诸于笔,对于如何写一个爬虫框架,我想一步一步地结合具体代码来讲述如何从零开始编写一个自己的爬虫框架年到如今,我花精力比较多的一个开源项目算是了,这是 showImg(https://segmentfault.com/img/remote/1460000018513379); 这篇文章的题目有点大...
?????? ???Hello,大家好我叫是Dream呀,一个有趣的Python博主,小白一枚,多多关照??? ???CSDN Python领域新星创作者,大二在读,欢迎大家找我合作学习 ?入门须知:这片乐园从不缺乏天才,努力才是你的最终入场券!??? ?最后,愿我们都能在看不到的地方闪闪发光,一起加油进步??? ???一万次悲伤,依然会有Dream,我一直在最温暖的地方等你,唱的就是我!哈哈哈~...
摘要:爬虫架构架构组成管理器管理待爬取的集合和已爬取的集合,传送待爬取的给网页下载器。网页下载器爬取对应的网页,存储成字符串,传送给网页解析器。从文档中获取所有文字内容正则匹配后记爬虫基础知识,至此足够,接下来,在实战中学习更高级的知识。 前言 Python非常适合用来开发网页爬虫,理由如下:1、抓取网页本身的接口相比与其他静态编程语言,如java,c#,c++,python抓取网页文档的接...
摘要:爬取币世界标红快讯内容移动版引入依赖写你自己的数据库地址需要自己安装客户端数据库表名伪造成手机写你自己的文件地址插入了一条新数据无新数据产生写你自己的文件地址时间不一致宕机使用当前系统时间进行爬取时间一致正常运行主要要求掌握内容语法 爬取币世界标红快讯内容(移动版) # 引入依赖 from lxml import etree import requests import pymongo...
阅读 1033·2021-10-08 10:04
阅读 3490·2021-08-05 10:01
阅读 2250·2019-08-30 11:04
阅读 1739·2019-08-29 15:29
阅读 743·2019-08-29 15:12
阅读 1645·2019-08-26 12:11
阅读 3085·2019-08-26 11:33
阅读 1140·2019-08-26 10:23