【Python实战】Scrapy豌豆荚应用市场爬虫
对于给定的大量APP,如何爬取与之对应的(应用市场)分类、描述的信息?且看下面分解。
1. 页面分析
当我们在豌豆荚首页搜索框输入微信后,会跳转到搜索结果的页面,其url为http://www.wandoujia.com/search?key=%微信。搜索结果一般是按相关性排序的;所以,我们认为第一条搜索结果为所需要爬取的。紧接着,点进去后会跳转到页面http://www.wandoujia.com/apps/com.tencent.mm,我们会发现豌豆荚的APP的详情页,是www.wandoujia.com/apps/ + APP package组成。
让我们退回到搜索结果页面,分析页面元素,如图:

所有搜索结果在<ul>无序列表标签中,每一个搜索结果在<li>标签中。对应地,CSS选择器应为
'#j-search-list>li::attr(data-pn)'
接下来,我们来分析APP的详情页,APP的名称所对应的HTML元素如图:

APP类别的如图:

APP描述的如图:

不难得到这三类元素所对应的CSS选择器
.app-name>span::text
.crumb>.second>a>span::text
.desc-info>.con::text
通过上面的分析,确定爬取策略如下:
- 逐行读取APP文件,拼接搜索页面URL;
- 分析搜索结果页面,跳转到第一条结果对应的详情页;
- 爬取详情页相关结果,写到输出文件
2. 爬虫实现
分析完页面,可以coding写爬虫了。但是,若裸写Python实现,则要处理下载间隔、请求、页面解析、爬取结果序列化。Scrapy提供一个轻量级、快速的web爬虫框架,并很好地解决了这些问题;中文doc有比较详尽的介绍。
数据清洗
APP文件中,可能有一些名称不规整,需要做清洗:
# -*- coding: utf-8 -*-
import re
def clean_app_name(app_name):
space = u'\u00a0'
app_name = app_name.replace(space, '')
brackets = r'\(.*\)|\[.*\]|【.*】|(.*)'
return re.sub(brackets, '', app_name)
URL处理
拿清洗后APP名称,拼接搜索结果页面URL。因为URL不识别中文等字符,需要用urllib.quote做URL编码:
# -*- coding: utf-8 -*-
from appMarket import clean
import urllib
def get_kw_url(kw):
"""concatenate the url for searching"""
base_url = u"http://www.wandoujia.com/search?key=%s"
kw = clean.clean_app_name(kw)
return base_url % (urllib.quote(kw.encode("utf8")))
def get_pkg_url(pkg):
"""get the detail url according to pkg"""
return 'http://www.wandoujia.com/apps/%s' % pkg
爬取
Scrapy的爬虫均继承与scrapy.Spider类,主要的属性及方法:
- name,爬虫的名称,
scrapy crawl命令后可直接跟爬虫的名称,即可启动该爬虫 - allowed_domains,允许爬取域名的列表
- start_requests(),开始爬取的方法,返回一个可迭代对象(iterable),一般为scrapy.Request对象
- parse(response),既可负责处理response并返回处理的数据,也可以跟进的URL(以做下一步处理)
items为保存爬取后数据的容器,类似于Python的dict,
import scrapy
class AppMarketItem(scrapy.Item):
# define the fields for your item here like:
kw = scrapy.Field() # key word
name = scrapy.Field() # app name
tag = scrapy.Field() # app tag
desc = scrapy.Field() # app description
豌豆荚Spider代码:
# -*- coding: utf-8 -*-
# @Time : 2016/6/23
# @Author : rain
import scrapy
import codecs
from appMarket import util
from appMarket.util import wandoujia
from appMarket.items import AppMarketItem
class WandoujiaSpider(scrapy.Spider):
name = "WandoujiaSpider"
allowed_domains = ["www.wandoujia.com"]
def __init__(self):
self.apps_path = './input/apps.txt'
def start_requests(self):
with codecs.open(self.apps_path, 'r', 'utf-8') as f:
for app_name in f:
yield scrapy.Request(url=wandoujia.get_kw_url(app_name),
callback=self.parse_search_result,
meta={'kw': app_name.rstrip()})
def parse(self, response):
item = AppMarketItem()
item['kw'] = response.meta['kw']
item['name'] = response.css('.app-name>span::text').extract_first()
item['tag'] = response.css('.crumb>.second>a>span::text').extract_first()
desc = response.css('.desc-info>.con::text').extract()
item['desc'] = util.parse_desc(desc)
item['desc'] = u"" if not item["desc"] else item["desc"].strip()
self.log(u'crawling the app %s' % item["name"])
yield item
def parse_search_result(self, response):
pkg = response.css("#j-search-list>li::attr(data-pn)").extract_first()
yield scrapy.Request(url=wandoujia.get_pkg_url(pkg), meta=response.meta)
APP文件里的应用名作为搜索词,也应被写在输出文件里。但是,在爬取时URL有跳转,如何在不同层级间的Request传递变量呢?Request中的meta (dict) 参数实现了这种传递。
APP描述.desc-info>.con::text,extract返回的是一个list,拼接成string如下:
def parse_desc(desc):
return reduce(lambda a, b: a.strip()+b.strip(), desc, '')
结果处理
Scrapy推荐的序列化方式为Json。Json的好处显而易见:
- 跨语言;
- Schema明晰,较于'\t'分割的纯文本,读取不易出错
爬取结果有可能会有重复的、为空的(无搜索结果的);此外,Python2序列化Json时,对于中文字符,其编码为unicode。对于这些问题,可自定义Pipeline对结果进行处理:
class CheckPipeline(object):
"""check item, and drop the duplicate one"""
def __init__(self):
self.names_seen = set()
def process_item(self, item, spider):
if item['name']:
if item['name'] in self.names_seen:
raise DropItem("Duplicate item found: %s" % item)
else:
self.names_seen.add(item['name'])
return item
else:
raise DropItem("Missing price in %s" % item)
class JsonWriterPipeline(object):
def __init__(self):
self.file = codecs.open('./output/output.json', 'wb', 'utf-8')
def process_item(self, item, spider):
line = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.file.write(line)
return item
还需在settings.py中设置
ITEM_PIPELINES = {
'appMarket.pipelines.CheckPipeline': 300,
'appMarket.pipelines.JsonWriterPipeline': 800,
}
分配给每个类的整型值,确定了他们运行的顺序,按数字从低到高的顺序,通过pipeline,通常将这些数字定义在0-1000范围内。
【Python实战】Scrapy豌豆荚应用市场爬虫的更多相关文章
- python基于scrapy框架的反爬虫机制破解之User-Agent伪装
user agent是指用户代理,简称 UA. 作用:使服务器能够识别客户使用的操作系统及版本.CPU 类型.浏览器及版本.浏览器渲染引擎.浏览器语言.浏览器插件等. 网站常常通过判断 UA 来给不同 ...
- scrapy——7 scrapy-redis分布式爬虫,用药助手实战,Boss直聘实战,阿布云代理设置
scrapy——7 什么是scrapy-redis 怎么安装scrapy-redis scrapy-redis常用配置文件 scrapy-redis键名介绍 实战-利用scrapy-redis分布式爬 ...
- Python之Scrapy爬虫框架安装及简单使用
题记:早已听闻python爬虫框架的大名.近些天学习了下其中的Scrapy爬虫框架,将自己理解的跟大家分享.有表述不当之处,望大神们斧正. 一.初窥Scrapy Scrapy是一个为了爬取网站数据,提 ...
- dota玩家与英雄契合度的计算器,python语言scrapy爬虫的使用
首发:个人博客,更新&纠错&回复 演示地址在这里,代码在这里. 一个dota玩家与英雄契合度的计算器(查看效果),包括两部分代码: 1.python的scrapy爬虫,总体思路是pag ...
- Python实战:Python爬虫学习教程,获取电影排行榜
Python应用现在如火如荼,应用范围很广.因其效率高开发迅速的优势,快速进入编程语言排行榜前几名.本系列文章致力于可以全面系统的介绍Python语言开发知识和相关知识总结.希望大家能够快速入门并学习 ...
- 基于Python,scrapy,redis的分布式爬虫实现框架
原文 http://www.xgezhang.com/python_scrapy_redis_crawler.html 爬虫技术,无论是在学术领域,还是在工程领域,都扮演者非常重要的角色.相比于其他 ...
- python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)
一.scrapy框架简介 1. 介绍 Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速.简单.可扩展的方式从网站中提取所需的数据.但目前S ...
- 零基础入门Python实战:四周实现爬虫网站 Django项目视频教程
点击了解更多Python课程>>> 零基础入门Python实战:四周实现爬虫网站 Django项目视频教程 适用人群: 即将毕业的大学生,工资低工作重的白领,渴望崭露头角的职场新人, ...
- Python:Scrapy(二) 实例分析与总结、写一个爬虫的一般步骤
学习自:Scrapy爬虫框架教程(二)-- 爬取豆瓣电影TOP250 - 知乎 Python Scrapy 爬虫框架实例(一) - Blue·Sky - 博客园 1.声明Item 爬虫爬取的目标是从非 ...
随机推荐
- 模拟Post登陆带验证码的网站
前言: 作者在一个项目需求 模拟用户登陆,获取该用户的订单记录. 该系统需要用户名,密码,验证码 (验证码为正楷的数字4位),于是参考网络一些文章,并进行了很多测试,总结步骤如下: 步骤1 : 通过h ...
- jquery如何获取第一个或最后一个子元素?
通过children方法,children("input:first-child") 1 2 $(this).children("input:first-child&qu ...
- python字符串的使用
之前在网上看了关于python最基础的一些教程,看着都通俗易懂,但是在写的过程中却感觉还是很生涩.关于字符串的使用还是应该多写多练!如何将“teacher_id = 123 #老师ID”转换成字典或者 ...
- 解读ASP.NET 5 & MVC6系列(2):初识项目
初识项目 打开VS2015,创建Web项目,选择ASP.NET Web Application,在弹出的窗口里选择ASP.NET 5 Website模板创建项目,图示如下: 我们可以看到,此时Web ...
- 给Mac OS X的“逻辑宗卷组”改名
近日更新了Mac OS X的10.10版本——Yosemite,感觉良好,但当我在打开磁盘工具的时候发现了一个美中不足的地方,磁盘宗卷组的名字居然还是我之前的“Mavericks”,如图: 如何对它进 ...
- 《HiWind企业快速开发框架实战》(1)框架的工作原理
<HiWind企业快速开发框架实战>(1)框架的工作原理 1.HiWind架构 HiWind的基本架构如下: 持久层部分:同时为框架本身的业务服务,也为开发人员的自定义业务服务. 逻辑层: ...
- 为什么使用Binder而不是其他IPC机制
本文搬运自:Advantages of using Binder for IPC in Android 使用Binder而不是其他(Semaphores , Message Queue, PIPES) ...
- sqlalchemy(二)高级用法
sqlalchemy(二)高级用法 本文将介绍sqlalchemy的高级用法. 外键以及relationship 首先创建数据库,在这里一个user对应多个address,因此需要在address上增 ...
- [ASP.NET MVC 小牛之路]05 - 使用 Ninject
在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来 ...
- 说说设计模式~大话目录(Design Pattern)
回到占占推荐博客索引 设计模式(Design pattern)与其它知识不同,它没有华丽的外表,没有吸引人的工具去实现,它是一种心法,一种内功,如果你希望在软件开发领域有一种新的突破,一个质的飞越,那 ...