字号

scrapy

[TOC]

开始

scrapy安装

  1. 首先手动安装windows版本的Twisted

    https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

    pip install Twisted-18.4.0-cp36-cp36m-win_amd64.whl

  2. 安装scrapy

    pip install -i https://pypi.douban.com/simple/ scrapy

  3. windows系统额外需要安装pypiwin32

    pip install -i https://pypi.douban.com/simple pypiwin32

新建项目

开始一个项目

E:\svnProject> scrapy startproject TestSpider

生成一个新的爬虫(generate)

E:\svnProject> cd TestSpider
E:\svnProject\TestSpider> scrapy genspider dongfeng www.dongfe.com

启动一个爬虫

E:\svnProject\TestSpider> scrapy crawl dongfeng

SHELL模式

> scrapy shell http://www.dongfe.com/  # 命令行调试该网页

pycharm调试启动文件

E:\svnProject\TestSpider> vim main.py

import sys
import os
from scrapy.cmdline import execute BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(BASE_DIR) # scrapy crawl dongfeng
execute(["scrapy", "crawl", "dongfeng"])

项目基本配置

E:\svnProject\TestSpider\TestSpider> vim settings.py

ROBOTSTXT_OBEY = False  # 不要遵循网站robots文件

XPATH

表达式 说明
/body 选出当前选择器的根元素body
/body/div 选取当前选择器文档的根元素body的所有div子元素
/body/div[1] 选取body根元素下面第一个div子元素
/body/div[last()] 选取body根元素下面最后一个div子元素
/body/div[last()-1] 选取body根元素下面倒数第二个div子元素
//div 选取所有div子元素(不论出现在文档任何地方)
body//div 选取所有属于body元素的后代的div元素(不论出现在body下的任何地方)
/body/@id 选取当前选择器文档的根元素body的id属性
//@class 选取所有元素的class属性
//div[@class] 选取所有拥有class属性的div元素
//div[@class='bold'] 选取所有class属性等于bold的div元素
//div[contains(@class,'bold')] 选取所有class属性包含bold的div元素
/div/* 选取当前文档根元素div的所有子元素
//* 选取文档所有节点
//div[@*] 获取所有带属性的div元素
//div/a | //div/p 选取所有div元素下面的子元素a和子元素p(并集)
//p[@id='content']/text() 选取id为content的p标签的内容(子元素的标签和内容都不会获取到)

> 注意: XPATH在选择时,参考的是HTML源码,而不是JS加载后的HTML代码

操作例子

title_selector = response.xpath("//div[@class='entry-header']/h1/text()")
title_str = title_selector.extract()[0]

CSS选择器

表达式 说明
* 选择所有节点
#container 选择Id为container的节点
.container 选取所有包含container类的节点
li a 选取所有li下的所有后代a元素(子和孙等所有的都会选中)
ul + p 选取ul后面的第一个相邻兄弟p元素
div#container > ul 选取id为container的div的所有ul子元素
ul ~ p 选取与ul元素后面的所有兄弟p元素
a[title] 选取所有有title属性的a元素
a[href='http://taobao.com'] 选取所有href属性等于http://taobao.com的a元素
a[href*='taobao'] 选取所有href属性包含taobao的a元素
a[href^='http'] 选取所有href属性开头为http的a元素
a[href$='.com'] 选取所有href属性结尾为.com的a元素
input[type=radio]:checked 选取选中的radio的input元素
div:not(#container) 选取所有id非container的div元素
li:nth-child(3) 选取第三个li元素
tr:nth-child(2n) 选取偶数位的tr元素
a::attr(href) 获取所有a元素的href属性值

操作例子

h1_selector = response.css(".container h1::text")  # 选取h1标题的内容
h1_str = h1_selector.extract_first() # 取出数组第一个,如果没有为空

爬虫

爬取某网站文章列表例子

>>> vim ArticleSpider/spiders/jobbole.py

import scrapy
from scrapy.http import Request
from urllib import parse
import re
from ArticleSpider.items import ArticleItem
from ArticleSpider.utils.common import get_md5 # url转md5 class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['blog.jobbole.com']
start_urls = ['http://blog.jobbole.com/all-posts/'] def parse(self, response):
"""
文章列表页的文章链接解析
:param response:
:return:
"""
css = "#archive > .post > .post-thumb > a"
article_urls_selector = response.css(css) # 获取当前列表页所有文章的链接
for article_url_selector in article_urls_selector:
head_img_url = article_url_selector.css("img::attr(src)").extract_first() # 封面URL
head_img_full_url = parse.urljoin(response.url, head_img_url) # 封面图片完整URL
article_url = article_url_selector.css("a::attr(href)").extract_first("") # 文章URL
article_full_url = parse.urljoin(response.url, article_url) # 智能的拼接URL,相对地址直接对接;绝对地址只取出域名对接;完全地址不对接,直接获取。
yield Request(url=article_full_url, callback=self.article_parse, meta={"head_img_full_url": head_img_full_url}) # 请求文章详情页并设置回调函数解析内容和meta传参
next_url = response.css(".next.page-numbers::attr(href)").extract_first("")
if next_url:
yield Request(url=parse.urljoin(response.url, next_url), callback=self.parse) # 下一页文章列表使用递归 def article_parse(self, response):
"""
文章详情页的内容解析
:param response:
:return:
"""
title = response.css(".grid-8 .entry-header > h1::text").extract_first("") # 标题内容
add_time = response.css(".grid-8 .entry-meta p::text").extract_first("")
add_time_match = re.match("[\s\S]*?(\d{2,4}[/-]\d{1,2}[/-]\d{1,2})[\s\S]*", add_time)
if add_time_match:
add_time = add_time_match.group(1)
else:
add_time = add_time.strip()
content = response.css(".grid-8 .entry").extract_first("") # 文章内容
star = response.css("h10::text").extract_first("") # 点赞数
head_img_url = response.meta.get("head_img_full_url") # 封面URL,通过上一个解释器在回调时传参得到的数据 # 把数据整理到item
article_item = ArticleItem() # 实例化一个item
article_item["title"] = title
article_item["content"] = content # 把时间字符串转为可保存mysql的日期对象
try:
add_time = datetime.datetime.strptime(add_time, "%Y/%m/%d").date()
except Exception as e:
add_time = datetime.datetime.now().date()
article_item["add_time"] = add_time
article_item["star"] = star
article_item["head_img_url"] = [head_img_url] # 传递URL图片保存列表供ImagesPipeline使用
article_item["url"] = response.url
article_item["url_object_id"] = get_md5(response.url) # 获取url的md5值 yield article_item # 传递到pipeline。请看settings.py中ITEM_PIPELINES字典
# Item设计,类似于django的表单类
>>> vim ArticleSpider/items.py
import scrapy class ArticleItem(scrapy.Item):
title = scrapy.Field() # 标题
content = scrapy.Field() # 内容
add_time = scrapy.Field() # 文章添加时间
url = scrapy.Field() # 文章URL
url_object_id = scrapy.Field() # URL的MD5值
head_img_url = scrapy.Field() # 封面图URL
head_img_path = scrapy.Field() # 封面图本地路径
star = scrapy.Field() # 点赞数
>>> vim ArticleSpider/spiders/settings.py
# 修改配置文件,去掉这个地方的注释,当爬虫解析函数返回Item对象时,需要经过这个管道
# Item管道
ITEM_PIPELINES = { # item的pipeline处理类;类似于item中间件
'ArticleSpider.pipelines.ArticlespiderPipeline': 300, # 处理顺序是按数字顺序,1代表第一个处理
}
# URL转md5函数
>>> create ArticleSpider/utils/__init__.py # 公共工具包
>>> vim common.py
import hashlib
def get_md5(url): # 获取URL的MD5值
if isinstance(url, str): # 如果是Unicode字符串
url = url.encode("utf-8")
m = hashlib.md5()
m.update(url) # 只接受UTF-8字节码
return m.hexdigest()

图片自动下载

>>> vim ArticleSpider/settings.py

PROJECT_DIR = os.path.join(BASE_DIR, "ArticleSpider")
# 修改配置文件,去掉这个地方的注释,当爬虫解析函数返回Item对象时,需要经过这个管道
# Item管道
ITEM_PIPELINES = { # item的pipeline处理类;类似于item中间件
'ArticleSpider.pipelines.ArticlespiderPipeline': 300, # 处理顺序是按数字顺序,1代表第一个处理
'scrapy.pipelines.images.ImagesPipeline': 1,
} IMAGES_URLS_FIELD = "head_img_url" # 图片URL的字段名
IMAGES_STORE = os.path.join(PROJECT_DIR, "images") # 图片本地保存地址
# IMAGES_MIN_HEIGHT = 100 # 接收图片的最小高度
# IMAGES_MIN_WIDTH = 100 # 接收图片的最小宽度

图片自动下载自定义类

>>> vim ArticleSpider/settings.py

ITEM_PIPELINES = {
...
#'scrapy.pipelines.images.ImagesPipeline': 1,
'ArticleSpider.pipelines.ArticleImagePipeline': 1,
}
>>> vim ArticleSpider/pipelines.py
class ArticleImagePipeline(ImagesPipeline):
def item_completed(self, results, item, info):
if "head_img_url" in item: # 只处理有数据的URL
for ok, value in results: # 默认是多个图片URL,其实只传递了一个,所以results内只有一个
image_file_path = value["path"] # 获取图片保存的本地路径 item["head_img_path"] = image_file_path return item # 返回item,下一个pipeline接收处理

数据保存

把item数据导出到json文件中

vim ArticleSpider/pipelines.py
import codecs # 文件操作模块
import json
# 把item保存到json文件
class JsonWithEncodingPipeline(object):
def __init__(self):
self.file = codecs.open("article.json", 'w', encoding="utf-8") def process_item(self, item, spider):
lines = json.dumps(dict(item), ensure_ascii=False) + "\n" # 关闭ascii保存,因为有中文
self.file.write(lines)
return item def spider_closed(self, spider):
# 当爬虫关闭时
self.file.close()

注册到item管道配置中


vim ArticleSpider/settings.py ITEM_PIPELINES = {
'...',
'ArticleSpider.pipelines.JsonWithEncodingPipeline': 2,
}

使用自带的模块导出json文件

>>> vim ArticleSpider/pipelines.py
from scrapy.exporters import JsonItemExporter
class JsonExporterPipeline(object):
# 调用scrapy提供的json export导出json文件
def __init__(self):
self.file = open("articleexport.json", "wb")
self.exporter = JsonItemExporter(self.file, encoding="utf-8", ensure_ascii=False)
self.exporter.start_exporting() def process_item(self, item, spider):
self.exporter.export_item(item)
return item def close_spider(self, spider):
self.exporter.finish_exporting()
self.file.close()

注册到item管道配置中

vim ArticleSpider/settings.py

ITEM_PIPELINES = {
'...',
'ArticleSpider.pipelines.JsonExporterPipeline': 2,
}

使用mysql保存

# 安装mysql驱动
>>> pip install mysqlclient
# centos需要另外安装驱动
>>> sudo yum install python-devel mysql-devel

使用同步的机制写入mysql

import MySQLdb
class MysqlPipeline(object):
def __init__(self):
self.conn = MySQLdb.connect('dongfe.com', 'root', 'Xiong123!@#', 'article_spider', charset="utf8", use_unicode=True)
self.cursor = self.conn.cursor() def process_item(self, item, spider):
insert_sql = """
insert into article(title, url, add_time, star)
values (%s, %s, %s, %s)
"""
self.cursor.execute(insert_sql, (item['title'], item['url'], item['add_time'], item['star']))
self.conn.commit()

使用异步的机制写入mysql

import MySQLdb
import MySQLdb.cursors
from twisted.enterprise import adbapi
class MysqlTwistedPipline(object): def __init__(self, dbpool):
self.dbpool = dbpool @classmethod
def from_settings(cls, settings):
pass # 这个方法会把settings文件传进来
dbparms = dict(
host="dongfe.com",
db="article_spider",
user="root",
passwd="Xiong123!@#",
charset="utf8",
cursorclass=MySQLdb.cursors.DictCursor,
use_unicode=True,
)
dbpool = adbapi.ConnectionPool("MySQLdb", **dbparms) return cls(dbpool) def process_item(self, item, spider):
# 使用twisted将mysql插入变成异步执行
query = self.dbpool.runInteraction(self.do_insert, item)
query.addErrback(self.handle_error) # 处理异常 def do_insert(self, cursor, item):
# 执行具体的插入
insert_sql = """
insert into article(title, url, add_time, star)
values (%s, %s, %s, %s)
"""
cursor.execute(insert_sql, (item['title'], item['url'], item['add_time'], item['star'])) def handle_error(self, failure, item, spider):
# 处理异步插入的异常
print(failure)

item loader

直接将ItemCSS选择器绑定到一起,直接把选择出来的数据放入Item中。

item loader的一般使用

>>> vim ArticleSpider/spiders/jobbole.py

class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['blog.jobbole.com']
start_urls = ['http://blog.jobbole.com/all-posts/'] ... # 文章详情页的内容解析
def article_parse(self, response): # 通过item loader加载item
item_loader = ItemLoader(item=ArticleItem(), response=response) item_loader.add_css("title", ".grid-8 .entry-header > h1::text")
item_loader.add_css("content", ".grid-8 .entry")
item_loader.add_css("add_time", ".grid-8 .entry-meta p::text")
item_loader.add_value("url", response.url)
item_loader.add_value("url_object_id", get_md5(response.url))
item_loader.add_value("head_img_url", [head_img_url])
item_loader.add_css("star", "h10::text") article_item = item_loader.load_item()
yield article_item # 传递到pipeline。请看settings.py中ITEM_PIPELINES字典

> 使用Item Loader的两个问题:
>厦门叉车租赁公司哪家好
> 1. 原始数据需要处理
> 1. 解决办法:在Item内使用字段的处理器
> 2. 不管数据有几个,获取的是一个数组
> 1. 解决办法:在Item内字段处理器中使用TakeFirst()方法

配合item 的processor处理器的使用

>>> vim ArticleSpider/items.py
# MapCompose:可以调用多个函数依次运行
# TakeFirst: 与extract_first()函数一样,只选择数组第一个数据
# Join: 把数组用符号连接成字符串,比如Join(",")
from scrapy.loader.processors import MapCompose, TakeFirst, Join # add_time键处理函数
def date_convert1(value):
add_time_match = re.match("[\s\S]*?(\d{2,4}[/-]\d{1,2}[/-]\d{1,2})[\s\S]*", value)
if add_time_match:
add_time = add_time_match.group(1)
else:
add_time = value.strip()
return add_time def date_convert2(value):
try:
add_time = datetime.datetime.strptime(value, "%Y/%m/%d").date()
except Exception as e:
add_time = datetime.datetime.now().date()
return add_time class ArticleItem(scrapy.Item):
...
add_time = scrapy.Field(
input_processor=MapCompose(date_convert1, date_convert2), # 处理原始数据
output_processor=TakeFirst() # 只取数组中第一个数据
) # 文章添加时间
...

自定义item loader

> 可以设置默认的default_output_processor = TakeFirst()

>>> vim ArticleSpider/items.py

from scrapy.loader import ItemLoader
class ArticleItemLoader(ItemLoader):
# 自定义item loader
default_output_processor = TakeFirst() # 默认item处理器,什么都不做
def default_processor(value):
return value class ArticleItem(scrapy.Item):
...
head_img_url = scrapy.Field(
# 图像URL需要一个数组类型,不取第一个数据,定义一个默认处理器覆盖掉
# 另外注意在使用sql保存时,需要取出数组第一个
output_processor=default_processor
) # 封面图URL
...
##########################################################################################
>>> vim ArticleSpider/spiders/jobbole.py from ArticleSpider.items import ArticleItemLoader class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['blog.jobbole.com']

Python爬虫框架Scrapy学习笔记原创的更多相关文章

  1. 《精通Python爬虫框架Scrapy》学习资料

    <精通Python爬虫框架Scrapy>学习资料 百度网盘:https://pan.baidu.com/s/1ACOYulLLpp9J7Q7src2rVA

  2. 《Python3网络爬虫开发实战》PDF+源代码+《精通Python爬虫框架Scrapy》中英文PDF源代码

    下载:https://pan.baidu.com/s/1oejHek3Vmu0ZYvp4w9ZLsw <Python 3网络爬虫开发实战>中文PDF+源代码 下载:https://pan. ...

  3. 教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神

    本博文将带领你从入门到精通爬虫框架Scrapy,最终具备爬取任何网页的数据的能力.本文以校花网为例进行爬取,校花网:http://www.xiaohuar.com/,让你体验爬取校花的成就感. Scr ...

  4. 【转载】教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神

    原文:教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神 本博文将带领你从入门到精通爬虫框架Scrapy,最终具备爬取任何网页的数据的能力.本文以校花网为例进行爬取,校花网:http:/ ...

  5. Linux 安装python爬虫框架 scrapy

    Linux 安装python爬虫框架 scrapy http://scrapy.org/ Scrapy是python最好用的一个爬虫框架.要求: python2.7.x. 1. Ubuntu14.04 ...

  6. Python爬虫框架Scrapy实例(三)数据存储到MongoDB

    Python爬虫框架Scrapy实例(三)数据存储到MongoDB任务目标:爬取豆瓣电影top250,将数据存储到MongoDB中. items.py文件复制代码# -*- coding: utf-8 ...

  7. Python爬虫框架Scrapy教程(1)—入门

    最近实验室的项目中有一个需求是这样的,需要爬取若干个(数目不小)网站发布的文章元数据(标题.时间.正文等).问题是这些网站都很老旧和小众,当然也不可能遵守 Microdata 这类标准.这时候所有网页 ...

  8. python爬虫之Scrapy学习

    在爬虫的路上,学习scrapy是一个必不可少的环节.也许有好多朋友此时此刻也正在接触并学习scrapy,那么很好,我们一起学习.开始接触scrapy的朋友可能会有些疑惑,毕竟是一个框架,上来不知从何学 ...

  9. Python爬虫框架Scrapy

    Scrapy是一个流行的Python爬虫框架, 用途广泛. 使用pip安装scrapy: pip install scrapy scrapy由一下几个主要组件组成: scheduler: 调度器, 决 ...

随机推荐

  1. 微信授权获取用户openid前端实现

    近来,倒霉的后台跟我说让我拿个openid做微信支付使用,寻思很简单,开始干活.   首先引导用户打开如下链接,只需要将appid修改为自己的就可以,redirect_url写你的重定向url   h ...

  2. P1004 方格取数

    题目描述 设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放 人数字0.如下图所示(见样例): A 0 0 0 0 0 0 0 0 0 0 13 0 0 6 0 ...

  3. 小H和密码

    链接:https://www.nowcoder.com/acm/contest/72/B来源:牛客网 题目描述     小H在击败怪兽后,被一个密码锁挡住了去路     密码锁由N个转盘组成,编号为1 ...

  4. 1548: Design road (思维题 做法:三分找极值)

    1548: Design road Submit Page    Summary    Time Limit: 2 Sec     Memory Limit: 256 Mb     Submitted ...

  5. Debian 8 安装录屏软件kazam

    1 安装 $ sudo apt-get install kazam [sudo] password for z: 正在读取软件包列表... 完成 正在分析软件包的依赖关系树 正在读取状态信息... 完 ...

  6. 基于 HTML5 WebGL 的计量站三维可视化监控系统 Web 组态工控应用

    得益于 HTML5 WebGL 技术的成熟,从技术上对工控管理的可视化,数据可视化变得简单易行!完成对工控设备的管理效率,资源管理,风险管理等的大幅度提高,同时也对国家工业4.0计划作出有力响应! 如 ...

  7. git 源码学习(init-db) 提交版本号 083c516331

    写在前面的废话: 学完git之后,还是感觉云里雾里的,于是乎,就想到了通过学习git源码,来加深git的熟练度,同时学习一下c语言编程. git源码学习,逐步分析 这篇帖子是逐步分析git源码的,将g ...

  8. Mongodb 常用语法

    以下语法为在控制台使用的.假定有集合(相当于关系型数据库的库)qyxxcx,文档(相当于表)qyxx,jyzcjzs show dbs use qyxxcx db show collections d ...

  9. golang日志收集方案之ELK

    每个系统都有日志,当系统出现问题时,需要通过日志解决问题 当系统机器比较少时,登陆到服务器上查看即可满足 当系统机器规模巨大,登陆到机器上查看几乎不现实 当然即使是机器规模不大,一个系统通常也会涉及到 ...

  10. lua协程实现

    协程是个很好的东西,它能做的事情与线程相似,区别在于:协程是使用者可控的,有API给使用者来暂停和继续执行,而线程由操作系统内核控制:另外,协程也更加轻量级.这样,在遇到某些可能阻塞的操作时,可以使用 ...