爬虫概念
数据获取的方式:
- 企业生产的用户数据:大型互联网公司有海量用户,所以他们积累数据有天然优势。有数据意识的中小型企业,也开始积累的数据。
- 数据管理咨询公司
- 政府/机构提供的公开数据
- 第三方数据平台购买数据
- 爬虫爬取数据
什么是爬虫
抓去网页数据的程序
网页三大特征:
爬虫的设计思路
- 确定需要爬取的网页
URL
地址 - 通过
HTTP/HTTPS
协议来获取对应的HTML
页面 - 提取
HTML
页面中的数据
如果是需要的数据,就保存起来
如果页面是其它URL
,那就继续爬取
如何抓取HTML页面
HTTP协议请求的处理,urllib
, urllib2
, requests
,处理后的请求可以模拟浏览器发送请求,获取服务器响应的文件
解析服务器响应的内容re
, xpath
(常用), BeautifulSoup4(bs4)
, JSONpath
, pyquery
等使用某种描述性一样来给需要提取的数据定义一个匹配规则,符合这个规则的数据就会被匹配。
如何采集动态HTML,验证码的处理selenium
(自动化测试工具) + Phantomjs
(无界面浏览器)
验证码处理通过Tesseract
: 机器图像识别系统(图片中的文本识别)
Scrapy框架
(scrapy
, Pyspider
)
分布式策略
使用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&HTTPS
HTTP协议(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文件和相关的图形文件),不需要每次都去请求建立连接。
- Client 发起一个包含
- Upgrade-Insecure-Requests (升级为HTTPS请求): 升级不安全的请求,意思是会在加载 http 资源时自动替换成 https 请求,让浏览器不再显示https页面中的http请求警报。(HTTPS 是以安全为目标的 HTTP 通道,所以在 HTTPS 承载的页面上不允许出现 HTTP 请求,一旦出现就是提示或报错。)
- User-Agent (浏览器名称): 是客户浏览器的名称
-
Accept (传输文件类型): 指浏览器或其他客户端可以接受的MIME(Multipurpose Internet Mail Extensions(多用途互联网邮件扩展))文件类型,服务器可以根据它判断并返回适当的文件格式。
-
Accept: **;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()
-
0