博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用scrapy爬取ttlsa博文相关数据存储至mysql
阅读量:6267 次
发布时间:2019-06-22

本文共 8753 字,大约阅读时间需要 29 分钟。

运维生存时间这个博客内容还是比较详尽的,对与运维技术人员的我来说,是偶尔溜达进来的地方,从中也学习到不少知识,感谢博主的奉献!

这段时间我就通过scrapy来收集下此博客内文章的相关数据,供以后需要从中提取我认为值得看的文章作为数据依据.

今天,要做的事就是把数据先抓取出来,后期再将其数据存储起来.

首先通过命令

scrapy genspider ttlsa www.ttlsa.com创建一个蜘蛛程序应用名为ttlsa

其次在ttlsa.py下编写如下代码.

# -*- coding: utf-8 -*-import refrom urllib import parsefrom datetime import datetimeimport scrapyfrom scrapy.http import Requestfrom ScrapyProject.utils.common import get_object_id'''获取ttlsa文章相关数据'''class TtlsaSpider(scrapy.Spider):    name = 'ttlsa'    allowed_domains = ['www.ttlsa.com']    start_urls = ['http://www.ttlsa.com/']    def parse(self, response):        post_nodes = response.css("article")        for node in post_nodes:            front_img_url = node.css("figure:nth-child(1) > a:nth-child(1) > img:nth-child(1)::attr(src)").extract_first("")            #create_time = node.css("div:nth-child(3) > span:nth-child(3) > span:nth-child(1)::text").extract_first("")            url = node.css("figure:nth-child(1) > a:nth-child(1)::attr(href)").extract_first("")            url = parse.urljoin(response.url,url)            if  url != "http://www.ttlsa.com/":                yield Request(url=url,meta={"front_img_url": front_img_url}, callback=self.parse_detail)        next_page = response.css(".next ::attr(href)").extract_first("")        if next_page:            yield Request(url=parse.urljoin(response.url,next_page),callback=self.parse)    def parse_detail(self,response):        front_img_url = response.meta.get("front_img_url", "")        try:            create_time = response.css(".spostinfo ::text").extract()[3]            pattern = ".*?(\d+/\d+/\d+)"            m = re.match(pattern, create_time)            create_time = datetime.strptime(m[1], "%d/%m/%Y").date()        except IndexError:            create_time = datetime.now().date()        title = response.css(".entry-title::text").extract_first("")        #评论数        comment_nums = response.css("div.entry-content li.comment a::text").extract_first("0")        comment_nums=comment_nums.replace("发表评论", "0")        #点赞数        praise_nums = response.css("a.dingzan .count::text").extract_first("0").strip()        #tags        tags = ",".join(response.css("ul.wow li a::text").extract())        content = response.css(".single-content").extract()        from ScrapyProject.items import TtlsaItem        ttlsa_item = TtlsaItem()        ttlsa_item["title"] = title        ttlsa_item["comment_nums"] = comment_nums        ttlsa_item["praise_nums"] = praise_nums        ttlsa_item["tags"] = tags        ttlsa_item["content"] = content        ttlsa_item["create_time"] = create_time        ttlsa_item["front_img_url"] = [front_img_url]        ttlsa_item["url"] = response.url        ttlsa_item["url_object_id"] = get_object_id(response.url)        #使用yield,将会跳转到pipelines里执行相关类中,需要在settings.py中开启并且设置正确的ITEM_PIPELINES        yield ttlsa_item

items.py

class TtlsaItem(scrapy.Item):    title = scrapy.Field()    comment_nums = scrapy.Field()    praise_nums = scrapy.Field()    tags = scrapy.Field()    content = scrapy.Field()    create_time = scrapy.Field()    front_img_url = scrapy.Field()    #记录下载的图片本地路径    front_img_path = scrapy.Field()    url=scrapy.Field()    #因为url是固定长度,所以我们希望能获取一个固定长度的url对象值,供以后重复收集数据以判定是添加还是更新    url_object_id=scrapy.Field()

pipeline.py

class TtlsaPipeline(object):    def process_item(self,item,spider):        return item

settings.py

# Configure item pipelines# See https://doc.scrapy.org/en/latest/topics/item-pipeline.htmlITEM_PIPELINES = {   'ScrapyProject.pipelines.TtlsaPipeline': 300,  #注意这里的数字,越小越优先执行}

通过代码调试功能,便可以看到我们已经获取到我们想要的数据了.

用scrapy爬取ttlsa博文相关数据存储至mysql
并且在pipeline内打上断点,也能从pipeline中获取到数据了.
用scrapy爬取ttlsa博文相关数据存储至mysql
使用scrapy自带的pipeline下载图片,并且将其下载到本地,并且将图片路径保存到item中

1.重写pipeline

from scrapy.pipelines.images import ImagesPipeline,DropItemclass TtlsaImagesPipeline(ImagesPipeline):     def item_completed(self, results, item, info):        image_paths = [x['path'] for ok, x in results if ok]        if not image_paths:            raise DropItem("Item contains no images")        item[' front_img_path '] = image_paths        return item

2.设置settings.py

ITEM_PIPELINES = {    ' ScrapyProject.pipelines. TtlsaImagesPipeline ': 1,}IMAGES_URLS_FIELD = "front_img_url"project_dir=os.path.abspath(os.path.dirname(__file__))IMAGES_STORE = os.path.join(project_dir,"images")

3.在项目录下新建一个images目录,如图:

用scrapy爬取ttlsa博文相关数据存储至mysql
这样,我在抓取网站图片后,就可以将其下载到项目的images目录下了.
用scrapy爬取ttlsa博文相关数据存储至mysql
并且可以看到我们下载的图片路径存储到item[‘front_img_url’]中了.
用scrapy爬取ttlsa博文相关数据存储至mysql
对与此字段url_object_id=scrapy.Field()我们可以使用hashlib库来实现.
utils/common.py

import hashlibdef get_object_id(url):    md5 = hashlib.md5()    md5.update(url.encode('utf-8'))    return md5.hexdigest()if __name__ == '__main__':    print(get_object_id("http://www.baidu.com"))

用scrapy爬取ttlsa博文相关数据存储至mysql

这时候我们在ttlsa.py中这样改写.

from ScrapyProject.utils.common import get_object_id
ttlsa_item["url_object_id"] = get_object_id(response.url)
用scrapy爬取ttlsa博文相关数据存储至mysql
好了,该获取的数据都已经获取了,下面我们将其数据存储起来,供以后分析.

存储这块,我考略将其分2部分,第一部分存储到文件,另一部分存储到mysql

1.存储到文件

1.1修改pipelines.py

class JsonWithEncodingPipeline(object):    #自定义json文件的导出    def __init__(self):        self.file = codecs.open('ttlsa.json', 'w', encoding="utf-8")    def process_item(self, item, spider):        lines = json.dumps(dict(item), ensure_ascii=False) + "\n"        self.file.write(lines)        return item    def spider_closed(self, spider):        self.file.close()

1.2 修改settings.py

ITEM_PIPELINES = {
'ScrapyProject.pipelines.TtlsaImagesPipeline': 1,
'ScrapyProject.pipelines.JsonWithEncodingPipeline':2,
}
这样我们就可以将数据存储到ttlsa.json文件中了.
用scrapy爬取ttlsa博文相关数据存储至mysql
2.存储到mysql中
2.1设计存储mysql库ttlsa_spider表article
用scrapy爬取ttlsa博文相关数据存储至mysql
用scrapy爬取ttlsa博文相关数据存储至mysql
让我们编写一个MysqlPipeline,让其抓取的数据存储到mysql中吧.

import MySQLdbclass MysqlPipeline(object):    def __init__(self):        self.conn = MySQLdb.connect('localhost', 'root', 'root', 'ttlsa_spider', charset="utf8", use_unicode=True)        self.cursor = self.conn.cursor()    def process_item(self,item,spider):        insert_sql = """            insert into article(title,url,url_object_id,comment_nums,praise_nums,tags,content,create_time,            front_img_url,front_img_path)             values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)        """        self.cursor.execute(insert_sql,(item["title"], item["url"], item["url_object_id"], int(item["comment_nums"]),                                        int(item["praise_nums"]), item["tags"], item["content"], item["create_time"],                                        item["front_img_url"], item["front_img_path"]))        self.conn.commit()

注意:我们将评论数,占赞数强制转换成int类型了.

修改settings.py

ITEM_PIPELINES = {
'ScrapyProject.pipelines.TtlsaImagesPipeline': 1,
#'ScrapyProject.pipelines.JsonWithEncodingPipeline': 2,
'ScrapyProject.pipelines.MysqlPipeline': 3,

}

Debug跑下看看可有什么问题.

用scrapy爬取ttlsa博文相关数据存储至mysql
通过不断按F8,数据源源不断的流进数据库了,哈哈。

但是有一个问题,那就是当我们的数据量很大,大量的向数据库写入的时候,可能会导致数据库出现异常,这时我们应该使用异步的方式向数据库插入数据.下面我将使用异步插入的方式来重写pipeline.

首先们将数据库的配置文件写入到settings.py中.

#MYSQL
MYSQL_HOST="127.0.0.1"
MYSQL_USER="root"
MYSQL_PWD="4rfv%TGB^"
MYSQL_DB="ttlsa_spider"

后面我们如果想使用settings.py文件里定义的变量,可以在pipeline.py文件中的定义的类中使用from_settings(cls,settings)这个方法来获取.

from twisted.enterprise import adbapiclass MysqlTwsitedPipeline(object):    def __init__(self, dbpool):        self.dbpool = dbpool    @classmethod    def from_settings(cls, settings):        dbparms = {            'host': settings["MYSQL_HOST"],            'db': settings["MYSQL_DB"],            'user': settings["MYSQL_USER"],            'passwd': settings["MYSQL_PWD"],            'charset': 'utf8',            'use_unicode': True        }        dbpool=adbapi.ConnectionPool("MySQLdb", cp_min=10, cp_max=20, **dbparms)        return cls(dbpool)    def process_item(self,item,spider):        """        使用twisted将mysql插入变成异步执行        """        query = self.dbpool.runInteraction(self.doInsert,item)        query.addErrback(self.handle_error,item,spider) #处理异步写入错误    def handle_error(self,failurer,item,spider):        if failurer:            print(failurer)    def doInsert(self,cursor,item):        insert_sql = """             insert into article(title,url,url_object_id,comment_nums,praise_nums,tags,content,create_time,             front_img_url,front_img_path)              values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)         """        cursor.execute(insert_sql, (item["title"], item["url"], item["url_object_id"], int(item["comment_nums"]),                                         int(item["praise_nums"]), item["tags"], item["content"], item["create_time"],                                         item["front_img_url"], item["front_img_path"]))

再将MysqlTwsitedPipeline类写入到settings.py文件中.

ITEM_PIPELINES = {
'ScrapyProject.pipelines.TtlsaImagesPipeline': 1,
#'ScrapyProject.pipelines.JsonWithEncodingPipeline': 2,
'ScrapyProject.pipelines.MysqlTwsitedPipeline': 3,
}

调试代码.

用scrapy爬取ttlsa博文相关数据存储至mysql
好了,数据又源源不断的写到数据库中了.

再看下与数据库连接的数目:

用scrapy爬取ttlsa博文相关数据存储至mysql
数了一下,有12个。也就是在连接池中的数量是由cp_min=10,cp_max=20定义的.

到此,数据便存储到mysql中了.

如果想了解更多,请关注我们的公众号

公众号ID:opdevos
扫码关注
用scrapy爬取ttlsa博文相关数据存储至mysql

转载于:https://blog.51cto.com/5ydycm/2339437

你可能感兴趣的文章
Mysql索引会失效的几种情况分析
查看>>
LVM逻辑卷
查看>>
zoj3591 Nim(Nim博弈)
查看>>
canvas绘图
查看>>
poj - 3039 Margaritas on the River Walk
查看>>
bootstrap(5)关于导航
查看>>
Aptana插件在eclipse中安装
查看>>
jQuery-数据管理-删除事件
查看>>
下载器简单实例
查看>>
java实现分页工具类(JDBC)
查看>>
欧几里德算法与扩展欧几里德算法
查看>>
Tinkoff Internship Warmup Round 2018 and Codeforces Round #475 (Div. 2)
查看>>
通过kafka提供的命令来查看offset消费情况
查看>>
oracle数据库从入门到精通之四
查看>>
自定义圆形图片控件
查看>>
sharepoint 2013 补丁升级步骤
查看>>
asp.net core 2.0 web api基于JWT自定义策略授权
查看>>
Skype for Business Server 2015-04-前端服务器-3-安装-管理工具
查看>>
第12章代码《跟老男孩学习Linux运维:Shell编程实战》
查看>>
我们为什么从Python转到go?
查看>>