爬虫(英语:Web crawler),也称网络蜘蛛,网络爬虫是一种自动化的程序,它可以访问互联网上的数据并从中提取有用的信息。简单来说,爬虫就是将搜索引擎里的内容复制到自己的网站里,然后再进行修改,形成自己的网页,这样就可以达到快速获取大量信息的目的。
爬虫能够收集海量数据、深刻洞察大公司运营模式、以及实现自我成为行业第一的可能性。例如,国内知名互联网公司如阿里巴巴、京东、腾讯等都采用了爬虫技术,通过爬虫技术,它们不仅可以收集大量数据,还可以通过爬虫技术获得客户真实意愿、商品销售情况、企业竞争力、产品质量状况等。同时,通过分析爬虫收集的数据,他们也可以找出商机、规划市场策略、提升品牌知名度、制定产品升级策略等。另外,爬虫技术还有助于防范网络安全攻击、分析热点话题、收集大量新闻等。总之,爬虫具有很大的应用价值,是网络时代的信息获取利器。
根据网络爬虫的任务类型及其数据采集能力不同,通常可分为以下几类:
⒈ 目录型爬虫(Catalog Crawling): 通过检索已知的目录链接或索引页,自动发现其他页面地址并抓取;
⒉ 内容型爬虫(Content Crawling): 从指定URL开始,递归地获取所需页面上的链接,进一步抓取内容;
⒊ 混合型爬虫(Mixed Crawling): 将两种以上爬虫结合起来使用;
⒌ 增量型爬虫(Incremental Crawling): 只对新增或更新的内容进行爬取;
⒍ API 型爬虫(API Crawling): 通过爬取网站提供的 API,获取数据的高级接口形式。
各类爬虫的特点以及适用场景如下图所示:
作为一种高级编程语言,Python 的爬虫库非常丰富。相比于其他语言的库,比如 Java 中的 Apache HttpClient、Python 中的 Requests、Scrapy 等,Python 的爬虫库提供了更加便捷的接口、功能完善的文档、强大的调试工具以及社区活跃的社区支持。
除了提供的众多爬虫框架外,Python 还提供了一些额外的功能特性,包括异步 I/O 支持、Web 服务框架、Web 框架等,这些特性使得 Python 在爬虫领域处于领先地位。
在最简单的定义下,爬虫是一个机器人,用来帮助我们快速、有效地获取网页上的数据。它的基本工作流程是:
爬虫由两大部分构成:引擎和网页解析器。其中引擎负责按照一定的规则向指定的目标服务器发送请求,获取响应数据;而网页解析器则是负责对获取到的原始数据进行解析,从中抽取我们想要的数据。
引擎又叫作爬虫控制器,是指程序的主体部分,负责管理整个爬虫的运行过程,比如初始化参数、调度任务、记录日志、存储结果等。常用的引擎有 Scrapy、CrawlSpider 和 BeautifulSoup 等。
网页解析器就是用来解析网页内容的模块,一般情况下会包含 HTML 解析器、XML 解析器、JSON 解析器、文本解析器等,解析出的结果可以是文本、图片、视频、音频、表格等多种类型。常用的网页解析器有 Beautiful Soup、lxml 等。
爬虫系统往往需要分布式部署,通过多台服务器同时抓取数据,避免单点故障。对于某些限制爬虫的反爬机制,比如动态验证码识别、IP 封锁等,需要进行相应的处理。
爬虫算法就是指用来实现网页爬取功能的一系列规则和方法。以下是常用的爬虫算法:
该算法以起始 URL 为中心,首先将起始 URL 添加到队列中,并标记为“待访问”,然后依次访问队列中的 URL。如果某个 URL 下存在新的 URL 可以继续访问,则添加到队列中,并标记为“待访问”。一直执行到队列为空或没有“待访问”的 URL 时停止。
该算法以起始 URL 为中心,首先将起始 URL 添加到栈中,并标记为“待访问”,然后依次访问栈中的 URL。如果某个 URL 下存在新的 URL 可以继续访问,则添加到栈中,并标记为“待访问”。一直执行到栈为空或没有“待访问”的 URL 时停止。
聚焦爬虫主要基于关键字来搜索网页,它的工作流程是:先找到关键词所在的页面,然后抓取关键词周围一定范围的页面,经过筛选后将结果输出。当用户输入查询条件时,它就像一个小型的网页搜索引擎一样,查找相关信息。
模拟登录爬虫是指爬虫程序可以模拟用户登录网站,进行更多的操作。例如,可以爬取用户私信消息、购物记录等,在做科研工作、应急应变工作时十分有用。
数据挖掘爬虫利用数据挖掘的算法和技术,从网页源代码中自动提取有价值的信息。可以用于网页数据分析、智能产品推荐等方面。
首先需要引入 requests 库,然后利用 requests 库的 get() 方法请求页面,并得到返回值 response 对象。通过 response 对象的方法,我们可以获取页面的状态码 status_code、HTTP 头部 headers、cookies、内容 body、编码 charset、超时时间 timeout 等信息。
import requests
response = requests.get('http://www.example.com')
print(response.status_code) # 打印状态码
print(response.headers) # 打印 HTTP 头部
print(response.cookies) # 打印 cookies
print(response.content) # 打印内容 body
print(response.encoding) # 打印编码 charset
print(response.url) # 打印请求的 URL
print(response.history) # 打印重定向历史
利用 re 库中的 findall() 方法,可以利用正则表达式匹配网页内容。findall() 方法返回的是一个列表,列表元素是匹配成功的字符串。
import re
html = 'Some text here.
'
pattern = r'(.*?)
'
result = re.findall(pattern, html)
print(result[0]) # Some text here.
BeautifulSoup 是一个 Python 库,它提供对 HTML、XML 文件的解析。我们可以使用 BeautifulSoup 来解析网页内容,得到完整的 DOM 树,然后就可以利用 DOM 树进行各种操作。
lxml 是一个快速且轻量级的 XML 解析器,它使用 XPath 表达式来定位节点。我们可以使用 lxml 解析网页内容,得到完整的 XML 树,然后就可以利用 XPath 语法进行各种操作。
from bs4 import BeautifulSoup
html = '''Title
- Item 1
- Item 2
'''
soup = BeautifulSoup(html, 'html.parser')
title = soup.find('h1').text # Title
items = [item.text for item in soup.select('li')] # ['Item 1', 'Item 2']
scrapy 是一个 Python 库,它提供了一个 Web 抓取框架,让我们可以方便地编写爬虫程序。通过 scrapy 提供的接口和组件,我们可以快速开发爬虫程序。
import scrapy
class MySpider(scrapy.Spider):
name = "myspider"
start_urls = ["http://www.example.com"]
def parse(self, response):
title = response.xpath("//title/text()").extract()[0]
print("Title:", title)
items = []
for item in response.css("li"):
items.append(item.xpath(".//text()").extract())
return {"title": title, "items": items}
爬虫抓取的结果可以保存到文件、数据库、MongoDB 等,方便后续分析。
import json
data = {"title": "Example Page", "items": [{"name": "Item 1"}, {"name": "Item 2"}]}
with open('output.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=4)
本文将以最常用的那个股票交易数据爬虫为例,阐述爬虫实现的全部细节,希望能够帮助读者更好地理解爬虫的工作原理。
通过浏览器打开百度股票搜索页面,我们可以看到其网页结构如下图所示:
显然,我们要爬取的网页大致符合这种结构,其中包含以下标签:
,该标签用来显示股票的当前价格、涨幅等信息。
,该标签用来显示股票的中文名。...
,该标签用来显示当前股票的价格。...
,该标签用来显示股票的涨幅。...
,该标签用来显示股票的简介。
,该标签用来购买股票。因此,我们可以通过解析网页源码,获取相应的标签内容,进而爬取相应的股票信息。
为了爬取网页,我们首先需要安装 requests 库,我们可以使用 pip 命令安装 requests。命令如下:
pip install requests
导入 requests 库后,我们可以使用 get() 函数来获取网页内容,并得到返回值 response 对象。
import requests
url = 'https://quote.baidu.com/'
r = requests.get(url)
print(r.text) # 查看网页源代码
requests 返回的 response 对象有一个 text 属性,我们可以直接打印此属性查看网页源码。但是,这只是普通的文本形式,并不能很方便地获取到我们想要的信息。
因此,我们可以借助 BeautifulSoup 库来解析网页内容。首先,我们导入 BeautifulSoup 库:
from bs4 import BeautifulSoup
然后,我们使用 BeautifulSoup() 函数来解析网页内容,并得到 soup 对象。soup 对象是一种树形数据结构,每个对象都是一个节点,包含了标签的名称、内容和子节点等信息。
soup = BeautifulSoup(r.text, 'html.parser')
这里,我们传入的第一个参数是 r.text,表示要解析的网页内容;第二个参数是 'html.parser',表示解析器类型为 'html.parser'。
接着,我们可以使用 soup 的 find_all() 方法来获取所有的
divs = soup.find_all('div')
for div in divs:
if 'class' not in div.attrs or 'fl hgt10 w80' not in div['class']:
continue
stock = {}
price_tag = div.find('span', attrs={'class': 'blue'})
if price_tag is not None and len(price_tag.string.strip()):
stock['price'] = float(price_tag.string.replace(',', '').strip().split('$')[1])
gain_tag = div.find('span', attrs={'class':'red up'})
if gain_tag is not None and len(gain_tag.string.strip()):
stock['gain'] = int(float(gain_tag.string.replace('%', '').strip())) / 100 + 1
img_tag = div.find('img')
if img_tag is not None and'src' in img_tag.attrs:
stock['icon'] = img_tag['src']
a_tag = div.find('a')
if a_tag is not None and 'href' in a_tag.attrs:
url = 'https://quote.baidu.com/' + a_tag['href']
if '/stock/' in url:
symbol = url.split('/')[4].lower()
stock['symbol'] = symbol
info_url = 'https://vip.stock.finance.sina.com.cn/corp/go.php/vCI_StockStructureHistory/stockid/{}/displaytype/default.phtml'.format(symbol)
info_resp = requests.get(info_url)
info_soup = BeautifulSoup(info_resp.text, 'html.parser')
data_tag = info_soup.find('table', attrs={'class':'mkt m_tab2 fx_daohang'})
rows = data_tag.select('tr > td:nth-of-type(2)')
items = []
for row in rows[:-1]:
value = ''.join([s.strip() for s in row.stripped_strings])
try:
value = float(value)
except ValueError:
pass
items.append(value)
stock['history'] = {'date': [], 'open': [], 'close': [], 'high': [], 'low': [], 'volume': []}
i = 0
while i <= len(items)-11:
date = str(rows[i+1].contents[0]).strip()
if '-' in date:
year, month, day = map(int, reversed(date.split('-')))
stock['history']['date'].append('{}-{:02}-{:02}'.format(year, month, day))
else:
year, quarter = map(int, date.split('Q'))
months = [(quarter-1)*3 + j + 1 for j in range(3)]
dates = ['{}-{:02}-{}'.format(year, month, -1) for month in months][::-1]
stock['history']['date'].extend(dates)
o, h, l, c, v = items[i], items[i+1], items[i+2], items[i+3], items[i+4]
stock['history']['open'].append(o)
stock['history']['high'].append(h)
stock['history']['low'].append(l)
stock['history']['close'].append(c)
stock['history']['volume'].append(v)
i += 11
print(stock)
此段代码的主要逻辑如下: