Python爬虫框架Scrapy学习笔记原创

时间:2022-12-26 02:40:51
 字号

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&plus;源代码&plus;《精通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教程&lpar;1&rpar;—入门

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

  8. python爬虫之Scrapy学习

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

  9. Python爬虫框架Scrapy

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

随机推荐

  1. 菜鸟git学习

    基础知识: 安装github之后,所有的命令在git shell 中输入. E:\文档\GitHub [master +2 ~0 -0 !]> cd [ToDoList]E:\文档\GitHub ...

  2. 深入理解RunLoop

    网上看的一篇文章,写的真好,我得多看几次好好理解理解 膜拜大神,转载至此便于学习查看. 此处标明原文链接:http://blog.ibireme.com/2015/05/18/runloop/    ...

  3. 使用 UnrealPak&period;exe 创建 Pak文件方法

    看来各位摸UE4 的基佬们,也是被DLC搞得不要不要的呢,其实热更新PAK是很简单就可以实现的,虽然当时我也是弄了快一个月. 下面贴一段以前在 Runtime 状态下 Mount Pak的代码,希望能 ...

  4. Java并发编程核心方法与框架-ExecutorService的使用

    在ThreadPoolExecutor中使用ExecutorService中的方法 方法invokeAny()和invokeAll()具有阻塞特性 方法invokeAny()取得第一个完成任务的结果值 ...

  5. 【C语言入门教程】7&period;2 结构体数组的定义和引用

    7.2 结构体数组的定义和引用 当需要使用大量的结构体变量时,可使用结构体定义数组,该数组包含与结构体相同的数据结构所组成的连续存储空间.如下例所示: struct student stu_a[50] ...

  6. KMP&lowbar;Best Reward

    大意:把一个字符串分成两串,假如一个字符串是回文串就可以加上它的VALUE,否则它的VALUE为0: KMP的特点是可以求出前缀与后面的字符串是否匹配, 注意回文串的特点,所以当我们把回文串反转的时候 ...

  7. stardict词典(星际译王&rpar;

    sudo apt-get install stardict 下载词库: http://abloz.com/huzheng/stardict-dic/zh_CN/ 把下载的压缩包解压,以a为例cd /u ...

  8. IOS 用drawRect 画表格

    自定义一个View DrawLine DrawLine.h #import <UIKit/UIKit.h> @protocol gridTouchDelete <NSObject&g ...

  9. curl&lowbar;escape ---&gt&semi; 使用URL 编码给定的字符串

    curl_escape (PHP 5 >= 5.5.0) curl_escape — 使用 URL 编码给定的字符串 说明¶ string curl_escape ( resource $ch  ...

  10. PKM你的知识需要管理

    有一段时间没有更新技术博客了~,大脑中总感觉有点东西要写,却不知道从哪里开始写~至少写点东西,也算是一个阶段的成长 学习(充电过程).工作(知识变现过程)不是简单重复,永远都是最值得去反思.玩味的事儿 ...