python pool.map(卡塔尔(قطر‎对文本读写操作时是还

作者:编程技术

我解释一下这里我没有贴get_one_page()函数和parse_one_page()函数,觉得不重要。其实就是用pool.map()产生的多个进程把main函数每次执行得到的item写入文件时发生了乱序问题,比如这个item还没完全写入,就插入着写入了另一个item。求大佬解释

图片 1

爬虫思路:

首先进入到我的博客页面 fengzheng/default.html?page=([d] ),这个就是爬虫的规则,爬取default.html页面,page参数为数字的页面,这样无论有多少页都可以遍历到。当然,如果页面数量很少可以在start_urls列表中,将要爬取的页面都列出来,但是这样当博文数量增多就会出现问题,如下:

start_urls = [
       "http://www.cnblogs.com/fengzheng/default.html?page=1",
       "http://www.cnblogs.com/fengzheng/default.html?page=2",
       "http://www.cnblogs.com/fengzheng/default.html?page=3",
   ]

当爬取的网页具有规则定义的情况下,要继承CrawlSpider爬虫类,使用Spider就不行了,在规则定义(rules)时,如果要对爬取的网页进行处理,而不是简单的需要Url,这时,需要定义一个回调函数,在爬取到符合条件的网页时调用,并且设置follow=Ture,定义如下:

rules = (
        Rule(LinkExtractor(allow=('fengzheng/default.html?page=([d] )', ),),callback='parse_item',follow=True),
    )

回调函数名称为parse_item,在parse_item方法中,就是真正要分析网页html,获取需要的内容的时候了。观察页面,查看需要的信息在什么位置,如图:

图片 2

之后,分析网页源码,分析出xpath

图片 3

用如下代码找到所有的class为day的div,每一个就是一个博文区域:

posts = sel.xpath('//div[@id="mainContent"]/div/div[@class="day"]')

之后遍历这个集合,获取所需内容,其中注意一下几点:

  • 因为有中文内容,要对获取的内容进行encode("utf-8")编码

  • 由于评论数和阅读量混在一起,要对那个字符串再进行正则表达式提取 

至此,简单的爬虫已经完成,接下来要运行这个爬虫,cd进入到爬虫项目所在的目录,执行以下命令:

scrapy crawl cnblogsSpider

会输出爬取过程信息

图片 4

之后会看到,根目录中多了一个item.json文件,cat此文件内容,可以看到信息已经被提取出来:

图片 5

更多入门教程可以参考 ()

最近在看崔庆才老师的爬虫课程第一节他用requests爬取猫眼电影top100,他最后用multiprocessing的Pool模块的map方法引入多进程,将爬取结果存到了文本文件,但是我发现最后文本文件的内容里,电影排序是乱序的,我在想是不是因为多进程读写文件时没有加锁的问题,还请各位大佬帮忙看看我贴一下主要代码

图片 6

安装scrapy

scrapy是第三方库,安装的时候可以是用pip,如下:

pip install scrapy
defmain(offset):url='' str(offset)html=get_one_page(url)#获取单页内容foriteminparse_one_page(html):#解析单页内容print(item)write_to_file(item)if__name__=='__main__':pool=Pool()pool.map(main,[i*10foriinrange(10)])

图片 7

题记

我们为什么需要一个爬虫框架?一个爬虫框架能帮我们做什么事情,减轻我们什么样的一些工作,我们重新梳理爬虫到底是做什么的,哪些工作是重复的工作,我们可以用框架来解决的。

一个简单的爬虫,从一个url开始,从开始的网页里面解析到所有可能的链接,通过这些链接不断的递归,抓取到全网的网页。

在这个过程中,

  • 需要考虑抓取的url重复问题,在不断扩张后的网页中,可能包含在我们已经抓取的网页地址,而这些地址我们是不希望他在重新抓取一遍。

  • 获取网页模块,一个好的获取网页模块,会影响到这个爬虫的质量,而这一部分可以单独出来,重复使用。

  • 抓取的网页后,需要进行对网页进行清洗,得到我们需要的信息。

  • 得到了我们需要的信息以后,可能需要把它保存在文件中或者是数据库中等等。

  • 有些特定的需求,项目本身对某些网页并不感兴趣,爬虫可以进行对爬取的链接进行过滤。

这就是scrapy 爬虫可以为我们做的事情。

图片 8

HTML解析器:利用re正则表达式解析出有效数据

创建一个一个项目

执行如下命令创建一个Scrapy项目

scrapy startproject scrapy_cnblogs

创建之后查看项目的目录结构如下:

scrapy_cnblogs
   ├── botcnblogs
   │   ├── __init__.py
   │   ├── items.py       #用于定义抓取内容的实体
   │   ├── pipelines.py   #处理抓取的item的管道
   │   ├── settings.py    #爬虫需要的配置参数在这里
   │ └── spiders
   │   └── __init__.py
   └── scrapy.cfg            #项目的配置文件,可以不去理会,默认即可

对照框架的好处,来理解各个文件的用途。

其中scrapy.cfg所在的目录为项目的根目录,此文件是项目的配置文件,项目建立后,此文件的内容可以不用理会。其内容如下:

[settings]
default = botcnblogs.settings

[deploy]
#url = http://localhost:6800/
project = botcnblogs

在items.py文件里定义在抓取网页内容中抽象出来的数据结构的定义,由于这里需要博客名称、发布日期、阅读量和评论量这四个字段,定义的Item结构如下:

from scrapy import Item,Field   #引入Item、Field

class BotcnblogsItem(Item):
    # define the fields for your item here like:
    title = Field()        #标题
    publishDate = Field()  #发布日期
    readCount = Field()    #阅读量
    commentCount = Field() #评论数<br><br>

在pipelines.py里对爬虫抓取到的信息(这里的信息是已经组织好的上面定义的Item对象)进行处理,官方介绍的典型的应用场景为:

  • 清理HTML数据

  • 验证爬取的数据(检查item包含某些字段)

  • 查重(并丢弃)

  • 将爬取结果保存到数据库中

它的定义也很简单,只需要实现process_item方法即可,此方法有两个参数,一个是item,即要处理的Item对象,另一个参数是spider,即爬虫。

另外还有open_spider和close_spider两个方法,分别是在爬虫启动和结束时的回调方法。

本例中处理很简单,只是将接收的Item对象写到一个json文件中,在__init__方法中以“w ”的方式打开或创建一个item.json的文件,然后把对象反序列化为字符串,写入到item.json文件中。代码如下:

# -*- coding: utf-8 -*- 
import json

class BotcnblogsPipeline(object):
    def __init__(self):
        self.file = open("item.json", "w ")

    def process_item(self, item, spider):
        record = json.dumps(dict(item), ensure_ascii=False) "n"  #此处如果有中文的话,要加上ensure_ascii=False参数,否则可能出现乱码
        self.file.write(record)
        return item

    def open_spider(self, spider):
        pass

    def close_spider(self, spider):
        self.file.close()

setting.py是爬虫的配置文件,配置爬虫的一些配置信息,这里用到的就是设置pipelines的ITEM_PIPELINES参数,此参数配置项目中启用的pipeline及其执行顺序,以字典的形式存在,{“pipeline”:执行顺序整数}

此例中的配置如下:

SPIDER_MODULES = ['botcnblogs.spiders']
NEWSPIDER_MODULE = 'botcnblogs.spiders'

ITEM_PIPELINES = {
'botcnblogs.pipelines.BotcnblogsPipeline': 1,
}

准备工作都做好了,爬虫呢,爬虫在哪里实现呢,我们看到项目中有个spiders目录,里面只有一个init.py文件,没错,爬虫文件需要自己创建,就在这个目录下,这里创建一个botspider.py的文件,对网页进行解析的工作就要在这里实现了,此例中定义的爬虫类继承自CrawlSpider类。

定义一个Spider需要如下几个变量和方法实现:

name:定义spider名字,这个名字应该是唯一的,在执行这个爬虫程序的时候,需要用到这个名字。

allowed_domains:允许爬取的域名列表,例如现在要爬取博客园,这里要写成cnblogs.com

start_urls:爬虫最开始爬的入口地址列表。

rules:如果要爬取的页面不是单独一个或者几个页面,而是具有一定的规则可循的,例如爬取的博客有连续多页,就可以在这里设置,如果定义了rules,则需要自己定义爬虫规则(以正则表达式的方式),而且需要自定义回调函数。

代码说话:

#-*- coding:utf-8 -*-
__author__ = 'linuxfengzheng'

from scrapy.spiders import Spider, Rule
from scrapy.selector import Selector
from botcnblogs.items import BotcnblogsItem
from scrapy.linkextractors import LinkExtractor
import re
from scrapy.spiders import CrawlSpider

class botspider(CrawlSpider):
    name = "cnblogsSpider"   #设置爬虫名称

    allowed_domains = ["cnblogs.com"]  #设置允许的域名
    start_urls = [
        "http://www.cnblogs.com/fengzheng/default.html?page=3",  #设置开始爬取页面
    ]


    rules = (
        Rule(LinkExtractor(allow=('fengzheng/default.html?page=([d] )', ),),callback='parse_item',follow=True),
    )  #制定规则


    def parse_item(self, response):
        sel = response.selector
        posts = sel.xpath('//div[@id="mainContent"]/div/div[@class="day"]')
        items = []
        for p in posts:
            #content = p.extract()
            #self.file.write(content.encode("utf-8"))
            item = BotcnblogsItem()
            publishDate = p.xpath('div[@class="dayTitle"]/a/text()').extract_first()

            item["publishDate"] = (publishDate is not None and [publishDate.encode("utf-8")] or [""])[0]
            #self.file.write(title.encode("utf-8"))
            title = p.xpath('div[@class="postTitle"]/a/text()').extract_first()
            item["title"] = (title is not None and [title.encode("utf-8")] or [""])[0]

            #re_first("posted @ 2015-11-03 10:32 风的姿态 阅读(d ")

            readcount  = p.xpath('div[@class="postDesc"]/text()').re_first(u"阅读(d )")

            regReadCount = re.search(r"d ", readcount)
            if regReadCount is not None:
                readcount = regReadCount.group()
            item["readCount"] = (readcount is not None and [readcount.encode("utf-8")] or [0])[0]

            commentcount  = p.xpath('div[@class="postDesc"]/text()').re_first(u"评论(d )")
            regCommentCount = re.search(r"d ", commentcount)
            if regCommentCount is not None:
                commentcount = regCommentCount.group()
            item["commentCount"] = (commentcount is not None and [commentcount.encode("utf-8")] or [0])[0]
            items.append(item)

        return items
        #self.file.close()

2.要写入特定编码的文本文件,请给open()函数传入encoding参数,将字符串自动转换成指定编码。细心的童鞋会发现,以'w'模式写入文件时,如果文件已存在,会直接覆盖(相当于删掉后新写入一个文件)。如果我们希望追加到文件末尾怎么办?可以传入'a'以追加(append)模式写入。

执行代码后,结果如下:

零基础小白到大神之路,欢迎加裙 474534951,群里有免费python课程,还有大量干货哦

def write_to_file(content): with open ('result.txt', 'a',encoding='utf-8') as f: f.write(json.dumps(content,ensure_ascii=False) ' ') f.close()

接下来就是构造主函数,初始化各个模块,传入入口URL,按照运行流程执行上面三大模块:

二、构造HTML解析器

三、构造数据存储器

def parse_one_page(html): pattern = re.compile('.*?board-index.*?>(d ).*?data-src="(.*?)".*?name">(.*?).*?star">(.*?).*?releasetime">(.*?)' '.*?integer">(.*?).*?fraction">(.*?).*?',re.S) items = re.findall(pattern,html) for item in items: yield{ 'index': item[0], 'image': item[1], 'title': item[2], 'actor': item[3].strip()[3:], 'time': item[4].strip()[5:], 'score': item[5] item[6] }

一、构造HTML下载器

HTML下载器:利用requests模块下载HTML网页;

注意:为了提高速度,我们引入Pool模块,用多线程并发抓取

def main(offset): url = '' str(offset) html = get_one_page(url) for item in parse_one_page(html): print(item) write_to_file(item)if __name__ == '__main__': p = Pool() p.map(main,[i*10 for i in range(10)])

数据存储器:将有效数据通过文件或者数据库的形式存储起来

对于Python初学者来说,爬虫技能是应该是最好入门,也是最能够有让自己有成就感的,今天,恋习Python的手把手系列,手把手教你入门Python爬虫,爬取猫眼电影TOP100榜信息,将涉及到基础爬虫架构中的HTML下载器、HTML解析器、数据存储器三大模块:

图片 9

注意事项:1.为什么ensure_ascii=False?原因是json默认是以ASCII来解析code的,由于中文不在ASCII编码当中,因此就不让默认ASCII生效;

注意:1.在函数中本来该return的地方用yield,如果用return,在第一轮循环就会跳出,结果文件只会有一部电影。如果用yield,函数返回的就是一个生成器,而生成器作为一种特殊的迭代器,可以用for——in方法,一次一次的把yield拿出来;

2.re.findall(pattern,string[,flags]):搜索整个string,以列表的形式返回能匹配的全部子串,其中参数是匹配模式,如re.S表示点任意匹配模式,改变“.”的行为。

import requestsfrom requests.exceptions import RequestExceptionheaders = {'User-Agent':'Mozilla/5.0 '}def get_one_page(url): try: res = requests.get(url,headers = headers) if res.status_code == 200: return res.text return None except RequestException: return None

本文中介绍了基础爬虫架构主要的的三个模块(HTML下载器、HTML解析器、数据存储器),无论大型还是小型爬虫都不会脱离这三个模块,也希望大家通过这个小小的练习对整个爬虫有个清晰的认识,欢迎大家一起谈论学习交流。

本文由美高梅4858官方网站发布,转载请注明来源

关键词: 文件 函数 进程 大佬 技术干