刀枪剑戟,斧钺钩叉,镋镰槊棒,鞭锏锤抓。css
神兵在手,妖魔不怕,劈荆斩棘,溅血生花。html
行走江湖,谁没有件趁手的兵器。python
可是,兵器有带楞的,有带刃儿的,有带戎绳的,有带锁链儿的,五花八门,对于新手来讲,真的是“乱花渐欲迷人眼”。git
不过,古有江湖百晓生,今有 Python 百媚生。百晓生所著的《兵器谱》让江湖血雨腥风,这百媚生也编纂了一部 Python 《神兵谱》,不知可否让 Python 江湖掀起什么暴雨狂风?github
咱们今天就来说讲这《神兵谱》的“数据分析”篇。这“数据分析”篇又分为上、中、下三篇,分别针对数据分析的数据采集、数据处理及数据可视化三个方面。web
本文不光是神兵的展现,更要教会你们简单的使用,可以帮助你们挑选合适趁手的兵器,才能在刀光剑影的江湖,立于不败之地。sql
话很少说,直入主题。数据库
说到数据采集,那最大名鼎鼎的方式就是“爬虫”啦,让咱们来看看百媚生带给咱们的“爬虫”利器吧,是否是真如传言的“见血封喉”呢?json
啥?为何 requests
是“爬虫”?后端
可不要小瞧了它!虽然说 requests
是网络请求库,但它却如高手手中的「木剑」通常,用好了,同样招招致命。
使用 requests
发起攻击(请求),犹如疾风般迅速,犹如落叶般轻盈。
>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
>>> r.status_code
200
>>> r.headers['content-type']
'application/json; charset=utf8'
>>> r.encoding
'utf-8'
>>> r.text
'{"type":"User"...'
>>> r.json()
{'private_gists': 419, 'total_private_repos': 77, ...}
复制代码
这就完了?
若是对方是返回 Json 格式的 API 服务,是的,这就完了。咱们已经拿到数据了。
若是对方是返回 XML 格式的 API 服务,那么,咱们再搭配上原生的 xml
或者 lxml
解析器,灭敌于百步以外。
""" content 是 xml 格式的字符串,即 r.text 例如 <?xml version="1.0"?> <data> <country name="a"></country> <country name="b"></country> <country name="c"></country> </data> """
import xml.etree.ElementTree as ET
tree = ET.parse(content)
root = tree.getroot()
# 遍历节点
for child in root:
print(child.tag, child.attrib)
复制代码
而 lxml
更快更凶残。
from lxml import etree
root = etree.XML(content)
for element in root.iter():
print("%s - %s" % (element.tag, element.text))
复制代码
lxml
更是支持强大的 xpath
和 xlst
语法(语法文档详见参考)。
# 使用 xpath 语法快速定位节点,提取数据
r = root.xpath('country')
text = root.xpath('country/text()')
复制代码
xlst
进行快速转换。
xslt_root = etree.XML('''\ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <foo><xsl:value-of select="/a/b/text()" /></foo> </xsl:template> </xsl:stylesheet>''')
transform = etree.XSLT(xslt_root)
f = StringIO('<a><b>Text</b></a>')
doc = etree.parse(f)
result_tree = transform(doc)
复制代码
对手更凶残了,是 HTML 文档!这下就须要 BeautifulSoup
或 lxml
解析器出马了。
BeautifulSoup
虽然速度不快,好在利于理解。
from bs4 import BeautifulSoup
# content 即 html 字符串, requests 返回的文本 text
soup = BeautifulSoup(content, 'html.parser')
print(soup.title)
print(soup.title.name)
print(soup.find_all('a'))
print(soup.find(id="link3"))
for link in soup.find_all('a'):
print(link.get('href'))
复制代码
上房揭瓦(解析网页),那是手到擒来。
而用 lxml
仍是那么干净利落。
html = etree.HTML(content)
result = etree.tostring(html, pretty_print=True, method="html")
print(result)
# 接下来就是 xpath 的表演时间
复制代码
可见,木剑虽朴实,在高手手中,也能变化多端。若是是“接骨木”,那更是了不起。最快速便捷的数据采集神兵,非 requests
莫属!
接下来让咱们看看数据采集的百变神兵 —— Scrapy,分分钟让咱们全副武装。
# 建立一个项目
scrapy startproject tutorial
cd tutorial
# 建立一个爬虫
scrapy genspider quotes quotes.toscrape.com
复制代码
而后编辑项目下 spiders/quotes.py
爬虫文件。
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
""" 生成初始请求。 """
urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
""" 处理请求返回的响应。 """
page = response.url.split("/")[-2]
filename = 'quotes-%s.html' % page
with open(filename, 'wb') as f:
f.write(response.body)
self.log('Saved file %s' % filename)
复制代码
而后就是启动爬虫。
scrapy crawl quotes
复制代码
这尚未发挥 Scrapy
的能力呢!
解析网页
# CSS 解析
response.css('title::text').getall()
# xpath 解析
response.css('//title/text()').getall()
复制代码
自动生成结果文件
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
def parse(self, response):
# parse 函数直接返回字典或者 Item 对象。
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
复制代码
在爬取的命令上加上 -o
参数,便可快速将结果保存到文件,支持多种格式(csv,json,json lines,xml),也可方便地扩展本身的格式。
scrapy crawl quotes -o quotes.json
复制代码
数据分页了,还有下一页怎么办?抛出请求,让 Scrapy 本身去处理。
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
]
def parse(self, response):
""" parse 函数 yield 字典或者 Item 对象,则视为结果, yield 请求对象(follow 方法便是跟随连接,快速生成对应的请求对象)即继续爬取。 """
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('span small::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
next_page = response.css('li.next a').get()
if next_page is not None:
yield response.follow(next_page, callback=self.parse)
复制代码
这就完了吗?固然不会,Scrapy 还提供了多种数据采集须要用到的功能。
这只是核心功能,还没见到它的社区能力呢!
这些就再也不展开了。
快速而又强大的数据采集利器,当属 Scrapy
!
强大的瑞士军刀 —— Pyspider。
Pyspider 可不得了,它提供了一整套完整的数据采集解决方案,堪称爬虫界的“瑞士军刀”。
爬虫,就是这么简单!
from pyspider.libs.base_handler import *
class Handler(BaseHandler):
crawl_config = {
}
@every(minutes=24 * 60)
def on_start(self):
self.crawl('http://scrapy.org/', callback=self.index_page)
@config(age=10 * 24 * 60 * 60)
def index_page(self, response):
for each in response.doc('a[href^="http"]').items():
self.crawl(each.attr.href, callback=self.detail_page)
def detail_page(self, response):
return {
"url": response.url,
"title": response.doc('title').text(),
}
复制代码
启动爬虫框架。
pyspider
复制代码
而后,咱们就能够经过 http://localhost:5000/
进行爬虫的管理和运行了。
咱们可使用 css 选择器快速提取网页信息。
def index_page(self, response):
for each in response.doc('a[href^="http"]').items():
if re.match("http://www.imdb.com/title/tt\d+/$", each.attr.href):
self.crawl(each.attr.href, callback=self.detail_page)
self.crawl(response.doc('#right a').attr.href, callback=self.index_page)
def detail_page(self, response):
return {
"url": response.url,
"title": response.doc('.header > [itemprop="name"]').text(),
"rating": response.doc('.star-box-giga-star').text(),
"director": [x.text() for x in response.doc('[itemprop="director"] span').items()],
}
复制代码
启用 PhantomJS
来渲染网页上的 JS。
pyspider phantomjs
复制代码
使用 fetch_type='js'
。
class Handler(BaseHandler):
def on_start(self):
self.crawl('http://www.twitch.tv/directory/game/Dota%202',
fetch_type='js', callback=self.index_page)
def index_page(self, response):
return {
"url": response.url,
"channels": [{
"title": x('.title').text(),
"viewers": x('.info').contents()[2],
"name": x('.info a').text(),
} for x in response.doc('.stream.item').items()]
}
复制代码
还能执行一段 JS 代码,来获取那些动态生成的网页内容。
class Handler(BaseHandler):
def on_start(self):
self.crawl('http://www.pinterest.com/categories/popular/',
fetch_type='js', js_script=""" function() { window.scrollTo(0,document.body.scrollHeight); } """, callback=self.index_page)
def index_page(self, response):
return {
"url": response.url,
"images": [{
"title": x('.richPinGridTitle').text(),
"img": x('.pinImg').attr('src'),
"author": x('.creditName').text(),
} for x in response.doc('.item').items() if x('.pinImg')]
}
复制代码
好了,接下来我知道,问题就是 Pyspider
和 Scrapy
选哪一个?
简单说下它们的对比。
Scrapy 有更强大的扩展能力,社区更活跃,周边更丰富。而 Pyspider 自己功能更全,但扩展能力较弱。许多 Scrapy 须要扩展实现的功能,如 Web 界面、JS 渲染等,Pyspider 原生都提供了。
Pyspider 的整套生态上手更容易,实现更快速。Scrapy 对复杂的场景有更多的选择余地,更灵活。
因此,诸位选哪款?
成年人须要作选择吗?
此上篇介绍了数据采集领域的三款神兵。
有此三款神兵在手,不信你不能驰骋“爬虫”的江湖!
百媚生 Python《神兵谱》之数据分析-上篇,若是以为有用,请点赞关注收藏哦!