精华内容
下载资源
问答
  • 整个系统大致可以分为三个部分:数据采集(前程无忧,智联招聘,拉勾),应用程序(tkinter),数据分析可视(pandas+Matplotlib) 数据采集:用scrapy采集招聘中的IT岗位信息。 应用程序:用tki...

    将自己最近做的系统好好介绍一番。。。

    刚开始想做的是爬虫,就是去爬取一些招聘网站的信息,但是感觉单独用一个Scrapy框架来完成工作量好像有点跟不上。于是就想加上数据分析。

    整个系统大致可以分为三个部分:数据采集(前程无忧,智联招聘,拉勾),应用程序(tkinter),数据分析可视(pandas+Matplotlib)

    数据采集:用scrapy采集招聘中的IT岗位信息。

    应用程序:用tkinter开发了登录界面与主界面。

    数据分析可视:用Pandas从不同维度分析数据库中数据,生成csv文件并进行保存,最后用Matplotlib读取csv文件,生成不同图表。

    下面贴一些效果图(选取一些模块分多篇文章详细介绍):

    登录成功后界面:

    数据分析可视部分截图:

    后面的博客会详细介绍。。。。。。。。。。。

    展开全文
  • python爬虫与数据分析实战scrapy多爬虫与数据分析实战爬虫思路一.分析目标网站二.创建爬虫项目,编写爬虫程序 scrapy多爬虫与数据分析实战 本次爬虫选择的网站是博主一直在玩的一个网页游戏的论坛,爬取游戏相关的所有...

    scrapy多爬虫与数据分析实战

    本次爬虫选择的网站是博主一直在玩的一个网页游戏的论坛,爬取游戏相关的所有玩家交流贴子和所有参与用户的信息.

    • 比较适合新手入门scrapy练手, 一共爬取了50000多篇帖子,和5000多名玩家信息.
    • 网站没有做一些反爬措施,(如果你看了这篇文章,想要尝试一下,也要稍微设置一下延迟啦~~~)
    爬虫思路
    • 分析目标网址结构和网站源代码
    • 创建爬虫项目,编写爬虫程序
      1. 这次实战有2个scrapy爬虫,一个爬取用户信息,一个爬取帖子内容
      2. 分别设置不同的ITEM_PIPELINES, 确保爬取的数据存放在不同的数据表中
      3. 连接mysql数据库,设置相关配置
      4. 设置爬虫全部同时启动命令
    • 使用pandas和matplotlib进行数据分析
      1. 制作帖子内容的词云图
      2. 绘制玩家在线时长条形图
      3. 绘制玩家创建账号时间与最后登录时间的折线图
      4. 绘制玩家论坛等级的条形分布图
    一.分析目标网站

    目标网站
    通过观察,可以看出
    所有的帖子url都在每一页的源代码中, 所以可以直接通过解析函数爬取第一个start_url获取帖子url,和下一页的url,然后遍历每一个帖子url进行内容的抓取,完成后再把下一页的url传给解析函数.
    点击其中的一个用户信息,我们发现每一个用户的个人信息url 都是由/space-uid-XXXXX.html结尾,很明显xxxx就是该用户的唯一标识uid,这样的话我们就可以使用scrapy中的crawl.spider进行url的正则匹配只要满足上诉条件都进行抓取(最开始想用requests做这个项目的时候,在这里本来打算构造url进行for循环,不幸的是uid从1到3000多w都有,而且部分uid的用户还不存在,果断放弃选择使用scrapy,虽然不能抓到所有的用户,但是活跃的用户都可以获取到)

    二.创建爬虫项目,编写爬虫程序

    通过第一步的观察,然后创建一个项目, 两个爬虫
    scrapy startproject lequ
    scrapy genspider LqSpider bbs.lequ.com
    scrapy genspider -t crawl ContentSpider bbs.lequ.com 注: 指定-t参数为crawl 匹配特定url爬虫
    在这里插入图片描述
    接下来开始编写项目,我使用的是pycharm+python3

    • 修改一些必要的配置项(其他的配置在写爬虫的时候再一起说)
      a.关闭robots协议: 在settings.py中将 ROBOTSTXT_OBEY = True 的值改为False
      b.设置访问延迟:在settings.py中将 #DOWNLOAD_DELAY = 3 取消注释并改为0.5
      c.设置随机请求头: 在middlewares.py中 编写如下代码

      from fake_useragent import UserAgent # 随机请求头第三方库,pip install fake-useragent安装
      
      class RandomUserAgent(object):
      
          def process_request(self, request, spider):
              """重写process_request方法,添加请求头信息
                  该方法是在爬虫访问url的时候调用,
              """
              # 创建一个随机请求头类的对象, 他的random属性返回一个随机请求头信息
              ua = UserAgent()
              request.headers['User-Agent'] = ua.random
              request.headers['Host'] = 'bbs.lequ.com'
      

      d.打开下载中间件配置: 在settings.py中将 #DOWNLOADER_MIDDLEWARES 这个字典 取消注释
      并将键值对改为’lequ.middlewares.RandomUserAgent’: 543 即使用我们自己重写的请求头类

    • 编写第一个用户信息爬虫
      经过第一步的分析,我们可以直接对start_url的解析结果进行xpath提取用户信息url和下一页url
      然后对每一页的用户信息url进行遍历,将遍历每一个url传递给一个解析详情页面的函数
      最后在把下一页的url传递给parse解析函数. 注:scrapy框架的爬虫默认第一个url即start_url的解析函数是parse方法
      关于xpath语法这里就不多介绍了,如果有同学比较迷惑,可以私信或者留言,注: 在命令行执行scrapy shell url, 可以启动scrapy交互模式,可以测试xpath语法的正确或者错误,url为要爬取的网页地址
      既然思路已经明确,那让我们愉快的开始吧

      ContentSpider.py

      import scrapy
      from scrapy.spiders import Spider
      from lequ.items import ContentItem
      import re
      
      
      class ContentSpider(Spider):
          name = 'content'
          allowed_domains = ['bbs.lequ.com']
          # 改为网站第一页的url
          start_urls = ['http://bbs.lequ.com/forum-173-1.html']
          # pipeline配置项,用来区分不同爬虫使用的pipeline的类
          custom_settings = {
              'ITEM_PIPELINES': {'lequ.pipelines.ContentPipeline': 300,}
          }
      
          def parse(self, response):
              # 提取当前页的所有帖子url存放在urls列表中, 注意当爬虫启动的时候首先会调用这个方法对start_url列表中的url进行爬取
              # 所以如果爬取的网站需要登录, 需要在这个方法中实现
              urls = response.xpath('//th[@class="common"]/a[1]/@href|//th[@class="new"]/a[1]/@href').getall()
              # 抓取下一页的url
              next_page = response.xpath('//a[@class="nxt"]/@href').get()
      
              for url in urls:
                  # 遍历urls,对每一个帖子链接传递给parse_detail函数解析
                  yield scrapy.Request(url, callback=self.parse_detail)
      
              # 判断是否存在下一页url,一般情况下最后一页是没有下一页链接的
              if next_page:
                  # 将下一页的url返回给parse函数进行解析,可以当作是递归的思想
                  yield scrapy.Request(next_page, callback=self.parse)
      
          def parse_detail(self, response):
      
              name = response.xpath('//div[@class="authi"]/a/text()').get()
              content = response.xpath('//div[@id="postlist"]/div[@id][1]//td[@class="t_f"]/text()').getall()
              content = re.sub(r'\s', '', ''.join(content))
              public_time = response.xpath('//em[@id][1]/text()').get().replace('发表于 ', '')
              read_count = int(response.xpath('//div[@class="hm ptn"]/span[2]/text()').get())
              reply_count = int(response.xpath('//div[@class="hm ptn"]/span[5]/text()').get())
              
              # 创建Item对象进行存放数据,博主一般喜欢先用xpath提取数据,在编写Item类,
              # 因为在提取的过程中可能还会有其他想法, 所以下面我们开始写Item类
              item = ContentItem(name=name, content=content, public_time=public_time,
                                 read_count=read_count, reply_count=reply_count)
      
              yield item
      if __name__ == '__main__':
          from scrapy.cmdline import execute
          # 创建执行爬虫命令, execute需要传入一个列表,即['scrapy', 'crawl', ContentSpider.name]
          # 直接执行这个文件,就等于在命令行启动这个爬虫, 方便我们来测试爬虫
          execute('scrapy crawl {}'.format(ContentSpider.name).split())
      

      Items.py

      class ContentItem(scrapy.Item):
      	# 这里定义scrapy存储字段,即想要存储哪些数据,就需要定义一个Fied(),要与爬虫文件中使用的变量名一致
      	# Item类似字典结构,可以使用字典的方法存储或者获取
          name = scrapy.Field()
          content = scrapy.Field()
          public_time = scrapy.Field()
          read_count = scrapy.Field()
          reply_count = scrapy.Field()
      
    • 编写第二个爬虫程序
      经过第一步的分析得知,我们可以通过匹配每一页中所有符合条件(用户信息url规则)的url进行爬取
      LqSpider.py

      from scrapy.linkextractors import LinkExtractor
      from scrapy.spiders import CrawlSpider, Rule
      from lequ.items import LequItem
      import re
      
      
      class LqspiderSpider(CrawlSpider):
          name = 'LqSpider'
          allowed_domains = ['bbs.lequ.com']
          # 改为网站第一页的url
          start_urls = ['http://bbs.lequ.com/forum-173-1.html']
          # pipeline配置项,用来区分不同爬虫使用的pipeline的类
          custom_settings = {
              'ITEM_PIPELINES': {'lequ.pipelines.LequPipeline': 300, }
          }
          # 将正则表达式的字符串形式编译为Pattern实例, 提高效率
          pattern = re.compile(r'[\(\)();;“”\s]')
          # url匹配规则
          rules = (
              # 在start_url中爬取满足.*forum-173-\d+.html的url, follow=True为跟进匹配,即当爬取的url中还存在满足表达式的url时继续爬取
              # 在这个规则中没有回调函数,是因为我们不需要解析页面内容,只需要获取到网页源码从中找出满足匹配规则的url即可
              Rule(LinkExtractor(allow=r'.*forum-173-\d+.html'), follow=True),
              # 在上面的每一个url中爬取满足.*space-uid-\d+.html的url,并传递给parse_html解析
              Rule(LinkExtractor(allow=r'.*space-uid-\d+.html'), callback='parse_html', follow=True),
          )
          # 注意,在写上一个爬虫中讲过,爬虫开始的时候默认调用parse方法爬取start_url,CrawlSpider爬虫模板已经实现了该方法,并对rules规则匹配
          def parse_html(self, response):
              
              name = response.xpath('//h2[@class="mt"]/text()').get() # 获取用户名
              name = self.pattern.sub('', name)
              active_time = int(response.xpath('//ul[@id="pbbs"]/li[1]/text()').get().split()[0]) # 获取在线时长
              create_time = response.xpath('//ul[@id="pbbs"]/li[2]/text()').get() # 获取创建时间
              last_login = response.xpath('//ul[@id="pbbs"]/li[3]/text()').get() # 获取最后登录时间
              last_activity = response.xpath('//ul[@id="pbbs"]/li[4]/text()').get() # 获取最后活跃时间
              area = response.xpath('//ul[@id="pbbs"]/li[6]/text()').get() # 获取地址信息
              area = self.pattern.sub('', area)
              identity = response.xpath('//span[contains(@style, "color")]//text()').getall()[-1] # 获取论坛等级
              uid_str = response.xpath('//span[@class="xw0"]/text()').get() # 获取uid
              uid = int(re.search(r'UID: (\d+)', uid_str).group(1))
              signature_info = response.xpath('//div[@class="pbm mbm bbda cl"]/ul[2]//td//text()').getall() # 获取个性签名
              signature = ''.join(signature_info) if signature_info else ''
              signature = self.pattern.sub('', signature)
              infos = response.xpath('//ul[@class="cl bbda pbm mbm"]//a/text()').getall() 
              friend_nums = int(infos[0].split()[-1]) # 获取好友数
              reply_times = int(infos[1].split()[-1]) # 获取回复数
              theme_nums = int(infos[2].split()[-1])  # 获取发帖数
              forum_money = int(response.xpath('//div[@id="psts"]/ul[@class="pf_l"]/li[last()]/text()').get().strip()) # 获取论坛币数量
              # 构造Item对象,存储数据
              item = LequItem(name=name, active_time=active_time, create_time=create_time, last_login=last_login,
                              last_activity=last_activity, area=area, identity=identity, uid=uid, signature=signature,
                              friend_nums=friend_nums, reply_times=reply_times, theme_nums=theme_nums, forum_money=forum_money)
              yield item
              print('{}的用户信息已爬取'.format(name))
      
      if __name__ == '__main__':
          from scrapy.cmdline import execute
          # 创建执行爬虫命令, execute需要传入一个列表,即['scrapy', 'crawl', LqspiderSpider.name]
          # 直接执行这个文件,就等于在命令行启动这个爬虫, 方便我们来测试爬虫
          execute('scrapy crawl {}'.format(LqspiderSpider.name).split())
      
    • 将数据存入数据库
      我们需要保存数据到文本或者数据库中,方便我们后续的数据分析,这里使用的是存入mysql数据库
      既然要保存数据,就需要用到pipeline
      pipeline.py

      # 导入python与mysql交互的第三方库
      import pymysql
      
      class MyPipeline(object):
          """
          因为两个爬虫需要存入到不同的表中,那么就需要两个pipeline类,
          但是可以发现,两个pipeline有一些方法是相同的,比如连接数据库,创建游标,关闭数据库等,
          那么可以自定义pipeline父类来实现这些操作,让这两个pipeline类继承于自定义的父类,简化代码
          pipeline需要实现3个方法,open_spider用于爬虫启动的时候执行,process_item接收到数据的时候执行,close_spider爬虫停止的时候执行
          """
          def __init__(self):
              # 连接mysql数据库
              self.conn = pymysql.Connect(
                  host='localhost', # 数据库ip
                  port=3306,      # 数据库端口号
                  user='root',    # 数据库用户名
                  passwd='XXXXX',  # 数据库密码
                  db='lequ',      # 要连接的数据库名
                  charset='utf8', # 编码
              )
              # 创建数据库游标操作数据库语句
              self.cursor = self.conn.cursor()
      
          	def open_spider(self, spider):
      
              pass
      
      	    def close_spider(self, spider):
              # 爬虫停止的时候关闭数据库
              self.conn.close()
              print('{}爬虫已停止'.format(self.__class__))
      
      class LequPipeline(MyPipeline):
          """用户信息内容爬虫Pipeline类"""
      
          def process_item(self, item, spider):
      		# 取出item中的数据
              name = item['name']
              create_time = item['create_time']
              active_time = item['active_time']
              last_login = item['last_login']
              last_activity = item['last_activity']
              area = item['area']
              signature = item['signature']
              friend_nums = item['friend_nums']
              reply_times = item['reply_times']
              theme_nums = item['theme_nums']
              identity = item['identity']
              uid = item['uid']
              forum_money = item['forum_money']
      		# 注意sql语句的写法,格式化的时候字符串类型的数据需要在{}外面加上引号(我不会告诉你,我之前没加,然后搞了几个小时才发现,哭....)
              sql = """insert into lequ_user (name, create_time, active_time, last_login, last_activity, area, signature, reply_times, theme_nums, identity, uid, forum_money, friend_nums) 
                                 values('{}', '{}', {}, '{}', '{}', '{}', '{}', {}, {}, '{}', {}, {}, {})"""
      
              self.cursor.execute(sql.format(name, create_time, active_time, last_login, last_activity,
                                                  area, signature, reply_times, theme_nums, identity, uid, forum_money, friend_nums))
              self.conn.commit()
      
      
      class ContentPipeline(MyPipeline):
          """帖子内容爬虫Pipeline类"""
          def process_item(self, item, spider):
      
              name = item['name']
              content = item['content']
              public_time = item['public_time']
              read_count = item['read_count']
              reply_count = item['reply_count']
      
              sql = """
                      insert into content (name, content, public_time, read_count, reply_count) 
                      values('{}', '{}', '{}', {}, {})"""
      
              self.cursor.execute(sql.format(name, content, public_time, read_count, reply_count))
      
              self.conn.commit()
      

    在settings.py中修改ITEM_PIPELINES = {
    ‘lequ.pipelines.LequPipeline’: 300,
    ‘lequ.pipelines.ContentPipeline’: 800,
    } 即 使用自己定义的pipeline类保存数据,要于爬虫文件中的类属性custom_settings的值保持一致,这样才能保证数据存入不同的数据表中

    OK, 到目前为止,我们已经写好两个scrapy爬虫,但是我们可能会想到一个问题,这样一个项目俩爬虫,还是要分开运行,和两个项目有什么区别阿,对哦, 我们还要写一个scrapy命令,让所有爬虫同时执行
    首先在爬虫文件夹同级目录下创建commands的python包
    然后创建crawlall.py的程序,可以直接把scrapy源代码中的commands代码复制进来,只改写run()即可

    import os
    from scrapy.commands import ScrapyCommand
    from scrapy.utils.conf import arglist_to_dict
    from scrapy.utils.python import without_none_values
    from scrapy.exceptions import UsageError
    
    
    class Command(ScrapyCommand):
        requires_project = True
    
        def syntax(self):
            return "[options] <spider>"
    
        def short_desc(self):
        	# 命令行执行scrapy -h 显示的描述
            return "Run all spider"
    
        def add_options(self, parser):
            ScrapyCommand.add_options(self, parser)
            parser.add_option("-a", dest="spargs", action="append", default=[], metavar="NAME=VALUE",
                              help="set spider argument (may be repeated)")
            parser.add_option("-o", "--output", metavar="FILE",
                              help="dump scraped items into FILE (use - for stdout)")
            parser.add_option("-t", "--output-format", metavar="FORMAT",
                              help="format to use for dumping items with -o")
    
        def process_options(self, args, opts):
            ScrapyCommand.process_options(self, args, opts)
            try:
                opts.spargs = arglist_to_dict(opts.spargs)
            except ValueError:
                raise UsageError("Invalid -a value, use -a NAME=VALUE", print_help=False)
            if opts.output:
                if opts.output == '-':
                    self.settings.set('FEED_URI', 'stdout:', priority='cmdline')
                else:
                    self.settings.set('FEED_URI', opts.output, priority='cmdline')
                feed_exporters = without_none_values(
                    self.settings.getwithbase('FEED_EXPORTERS'))
                valid_output_formats = feed_exporters.keys()
                if not opts.output_format:
                    opts.output_format = os.path.splitext(opts.output)[1].replace(".", "")
                if opts.output_format not in valid_output_formats:
                    raise UsageError("Unrecognized output format '%s', set one"
                                     " using the '-t' switch or as a file extension"
                                     " from the supported list %s" % (opts.output_format,
                                                                      tuple(valid_output_formats)))
                self.settings.set('FEED_FORMAT', opts.output_format, priority='cmdline')
    
        def run(self, args, opts):
            # 获取爬虫列表
            spd_loader_list = self.crawler_process.spider_loader.list()  # 获取所有的爬虫文件。
            print(spd_loader_list)
            # 遍历各爬虫
            for spname in spd_loader_list or args:
                self.crawler_process.crawl(spname, **opts.spargs)
                print('此时启动的爬虫为:' + spname)
            self.crawler_process.start()
    

    接下来新建cmd.py文件用来执行crawlall命令,或者可以直接再命令行下执行 scrapy crawlall

    from scrapy.cmdline import execute
    #启动这个文件就可以执行scrapy crawlall命令
    execute('scrapy crawlall'.split())
    

    最后还需要在settings.py中添加COMMANDS_MODULE = ‘lequ.commands’, 告诉scrapy自定义的命令在个目录
    OK, 爬虫全部完成,可以开始愉快的抓取数据了,顺便提一下博主用了11个小时才爬取完所有内容,鉴于篇幅问题,数据分析放到下一章中讲解.

    展开全文
  • 基于Scrapy的电商数据分析系统设计实现.pdf
  • 热销手机——Python爬虫数据分析 [环境] 视窗 蟒蛇3.8 [scrapy-spider] scrapy项目1:taobaoSpider scrapy项目2:jdSpider [数据资源] 来源:csv文件存储 [数据分析] 任务*.py [样本] 效果展示:task*.png
  • scrapy抓取数据存储至本地mysql数据库 基于python开发,采用scrapy数据存储至本地数据库(或excel表格) 程序的主要目的是完成抓取和分析的任务同时学习爬虫相关知识,所以在细节处理上略有不足,但考虑到最终的...
  • scrapy 豆瓣短评 数据分析 + 中文情感分析 + 可视化 (一) 一、scrapy 爬取 豆瓣短评 本次爬取的是哪吒之魔童降世 短评 。本次爬取的是静态网页还是蛮简单的。 1、开始地址 ... 爬取的内容 item设置为 ...

    scrapy 豆瓣短评 数据分析 + 中文情感分析 + 可视化 (一)

    一、scrapy 爬取 豆瓣短评
    本次爬取的是哪吒之魔童降世 短评 。本次爬取的是静态网页还是蛮简单的。
    1、开始地址

    https://movie.douban.com/subject/26794435/comments?status=P
    

    爬取的内容
    在这里插入图片描述

    item设置为

    class DoubanscrapyItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        table = 'douban'
        name = scrapy.Field()
        grade=scrapy.Field()
        content=scrapy.Field()
        time=scrapy.Field()
        support_num=scrapy.Field()
        pass
    

    爬取
    spider 为
    在这里插入图片描述

    class DoubanSpider(scrapy.Spider):
        name = 'douban'
    
        allowed_domains = ['movie.douban.com']
        start_urls = ['https://movie.douban.com/subject/26794435/comments?status=P']
    
        def __init__(self):
            self.start_url='https://movie.douban.com/subject/26794435/comments?status=P'
            self.next_url='https://movie.douban.com/subject/26794435/comments{next}'
    
    
        def start_requests(self):
            yield scrapy.Request(self.start_url, callback=self.get_parse)
    
        def get_parse(self, response):
            #print(response.body.decode('utf-8'))
    
            contexts=response.xpath('//*[@class="comment-item"]')
    
            for context  in contexts :
                item=DoubanscrapyItem()
                item["name"] = context.xpath(".//@title").extract_first()
                item["grade"] = context.xpath('.//*[@class="comment-info"]// span[2]/@title').extract_first()
                item["time"] = context.xpath('.//*[@class="comment-info"]//*[@class="comment-time "]/@title').extract_first()
                item["content"] = context.xpath('.//*[@class="short"]/text()').extract_first()
                item["support_num"]= context.xpath('.//*[@class="votes"]/text()').extract_first()
                yield  item
    
            next_page= context.xpath('//*[@id="paginator"]//*[@class="next"]/@href').extract_first()
            if next_page is not None :
                next= self.next_url.format(next=next_page)
    
                yield scrapy.Request(next, callback=self.get_parse)
    
    

    数据库的存储

       
    class MysqlPipeline():
        def __init__(self, host, database, user, password, port):
            self.host = host
            self.database = database
            self.user = user
            self.password = password
            self.port = port
    
        @classmethod
        def from_crawler(cls, crawler):
            return cls(
                host=crawler.settings.get('MYSQL_HOST'),
                database=crawler.settings.get('MYSQL_DATABASE'),
                user=crawler.settings.get('MYSQL_USER'),
                password=crawler.settings.get('MYSQL_PASSWORD'),
                port=crawler.settings.get('MYSQL_PORT'),
            )
    
        def open_spider(self, spider):
            self.db = pymysql.connect(self.host, self.user, self.password, self.database, charset='utf8',
                                      port=self.port)
            self.cursor = self.db.cursor()
    
        def close_spider(self, spider):
            self.db.close()
    
        def process_item(self, item, spider):
            data = dict(item)
            print(data)
            keys = ', '.join(data.keys())
            values = ', '.join(['%s'] * len(data))
            sql = 'insert into %s (%s) values (%s)' % (item.table, keys, values)
            self.cursor.execute(sql, tuple(data.values()))
            self.db.commit()
            return item
    
    
    
    

    爬取的数据如下
    在这里插入图片描述

    展开全文
  • TOP250豆瓣电影短评:Scrapy 爬虫 数据清理/分析 构建中文文本情感分析模型
  • 'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100, 'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300, 'scrapy.downloadermiddlewares.downloadtimeout....
  • 在没有看<利用python进行数据分析>之前一直不太明白数据分析是什么事情, 就跟学了很久python以后, 还是要时常搜索下, python能用来干嘛. 学了这两个模块后, 总算对于数据分析有一个初步的概念, 进行了一次实践 ...

    在没有看<利用python进行数据分析>之前一直不太明白数据分析是什么事情, 就跟学了很久python以后, 还是要时常搜索下, python能用来干嘛. 学了这两个模块后, 总算对于数据分析有一个初步的概念, 进行了一次实践

    注意 : 本文原则上不提供scrapy或者pandas的使用方法介绍.

    环境

    • python2.7
    • scrapy
    • pandas
    • matplotlib
    • windowns7
    • ipython

    目的

    获取指定贴吧的所有主题的发帖人, 发帖时间, 回复数, 帖子链接, 之后进行以下分析:

    • 每年总主题数
    • 每年发主题最多的用户
    • 每年回复最多的帖子
    • 每年每个主题平均回复数
    • 每年每个月的主题数

    具体操作

    数据挖掘


    新建item

    item我就理解成一个对象了, 但是是没有方法的对象, 或许称为一个bean更好.

    import scrapy
    class Tieba_hoter_item(scrapy.Item):
    title = scrapy.Field()
    author = scrapy.Field()
    date = scrapy.Field()
    times = scrapy.Field()
    href = scrapy.Field()
    

    item的建立紧扣我们的目的, 以一条帖子为一个item, 记录下它的属性.

    建立爬虫

    首先来分析我们信息的来源
    这里写图片描述

    还算简单, 不需要进帖子就可以获得回复数, 作者, 主题, 可惜没有时间(时间对我我们的分析非常重要).

    这里写图片描述

    进入帖子中, 发现了非常规范的时间显示, 正式我们需要的. 具体获得方式在下面代码中会有所体现, 这里不再赘述.

    之后我们建立爬虫. 上一篇的时候每次都手动设定headers, 这回搜寻资料, 了解到了一个属性DEFAULT_REQUEST_HEADERS, 这个是scrapy.Request的默认header设置. scrapy爬虫开始的连接是一个数组, 我决定用循环将所有页面添加进去

    start_urls = []
    pagesize = 50
    original_url = 'http://tieba.baidu.com/f?kw=clannad&ie=utf-8&pn='
    for x in xrange(4500):
        start_urls.append(original_url + str(x * pagesize))
    result_list = []
    

    之后是第一层解析, 要做的事情是获取标题, 链接, 回复数, 作者, 再根据链接进行第二层挖掘. 代码如下

    def parse(self, response):
            selector_title = response.css('.threadlist_title > a::attr(title)').extract()
            selector_href = response.css('.threadlist_title > a::attr(href)').extract()
            selector_timers = response.css('.threadlist_rep_num::text').extract()
            selector_author = response.css('.tb_icon_author::attr(title)').extract()
            if(len(selector_title) == (len(selector_author) if (len(selector_author) == len(selector_timers)) else False)):
                for i in xrange(len(selector_author)) :
                    item = Tieba_hoter_item(title = selector_title[i].encode('utf-8'), href = selector_href[i].encode('utf-8'), 
                        author = selector_author[i].encode('utf-8'), times = selector_timers[i].encode('utf-8'), date = self.temp_date)
                    yield scrapy.Request('http://tieba.baidu.com/' + selector_href[i], callback = (lambda r, x = item: self.parse_deal(r, x)), headers = self.DEFAULT_REQUEST_HEADERS)
            pass
    

    比较有难度的只有这一句

    yield scrapy.Request('http://tieba.baidu.com/' + selector_href[i], callback = (lambda r, x = item: self.parse_deal(r, x)), headers = self.DEFAULT_REQUEST_HEADERS)
    

    yield是根据官方文档写的, 还没有看源码, 应该是用生成器形式调用parse的. 其中费了一番力气的是这一句 callback = (lambda r, x = item: self.parse_deal(r, x)).

    先说一下我想做什么事情. 将parse()中获得的数据(标题, 作者等)传到第二级解析函数也就是callback函数parse_deal()中. 我曾经尝试将获得数据设置成class的成员变量. 可是scrapy使用了多线程, parse 跟 parse_deal 并不是线性进行. 最好的方式就是在callback中进行传值. 一开始不知道该怎么传(你可以试试直接写callback=parse_deal(xxxx)), 后来通过一番查找, 用lambda解决了这个问题. 这句代码的意思就是接收两个参数(第一个self), 第二个参数设置为item(我们需要下一级函数处理的数据).

    还剩下一件事情, 就是数据的本地化. 我放在了closed()函数中, 保证在任何原因(包括完成)爬虫停止的时候将数据存在本地.

    以下完整代码

    #-*- coding:utf-8 -*-
    import scrapy
    import urllib2
    import os
    from test1.items import Tieba_hoter_item
    import json
    import sys;reload(sys);sys.setdefaultencoding('utf-8')
    
    class TiebaSpider(scrapy.Spider):
        name = 'tieba'
        DEFAULT_REQUEST_HEADERS = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', 'Accept-Encoding': 'none', 'Accept-Language': 'en-US,en;q=0.8', 'Connection': 'keep-alive'}
        start_urls = []
        pagesize = 50
        original_url = 'http://tieba.baidu.com/f?kw=clannad&ie=utf-8&pn='
        for x in xrange(4500):
            start_urls.append(original_url + str(x * pagesize))
        result_list = []
        temp_date = ''
        def parse(self, response):
            selector_title = response.css('.threadlist_title > a::attr(title)').extract()
            selector_href = response.css('.threadlist_title > a::attr(href)').extract()
            selector_timers = response.css('.threadlist_rep_num::text').extract()
            selector_author = response.css('.tb_icon_author::attr(title)').extract()
            if(len(selector_title) == (len(selector_author) if (len(selector_author) == len(selector_timers)) else False)):
                for i in xrange(len(selector_author)) :
                    item = Tieba_hoter_item(title = selector_title[i].encode('utf-8'), href = selector_href[i].encode('utf-8'), 
                        author = selector_author[i].encode('utf-8'), times = selector_timers[i].encode('utf-8'), date = self.temp_date)
                    yield scrapy.Request('http://tieba.baidu.com/' + selector_href[i], callback = (lambda r, x = item: self.parse_deal(r, x)), headers = self.DEFAULT_REQUEST_HEADERS)
            pass
    
        def parse_deal(self, response, item):
            temp_date = response.css('.post-tail-wrap > .tail-info::text')[-1].extract()
            item['date'] = temp_date
            self.result_list.append(item)
            pass
    
        def closed(self, reason):
            print reason
            with open('tieba_temp/data.json', 'wb+') as f:
                f.write('[')
                for i in self.result_list:
                    f.write(json.dumps(dict(i), ensure_ascii = False) + ',')
                f.write('{}]')
                f.close()
    

    开爬

    用命令行启动爬虫, crawl什么的. 最后爬到了30MB左右的数据.

    数据分析


    将数据转换成DataFrame

    DataFrame是pandas基础数据结构, 与json的契合度很高.

    json_data = json.load(open('200000data.json'))
    df = DataFrame(json_data)
    

    数据规整

    我本来以为数据规整会花费很长的时间, 没想到爬到的数据意外规整(也因为数据不复杂啦). 只需要进行常规几部就可以开始进行分析了.

    df = df.dropna() #去除空行
    df = df.drop_duplicates() #去除重复行
    def me_parse_date(row):
        try: 
            return datetime.strptime(row, '%Y-%m-%d %H:%M')
        except:
            return np.nan
    
    def me_parse_num(row):
        try:
            return int(row)
        except:
            return np.nan
    
    df['times'] = df['times'].apply(me_parse_num) #times是回帖数, 需要参与计算, 原本数据类型为str, 需要转换为int
    df['date'] = df['date'].apply(me_parse_date) #date是发帖时间, 需要参与计算, 是最重要的数据标识, 需要转换为一致的时间格式
    

    这样我们的数据就规整结束, 可以开始进行数据分析了.

    每年总主题数

    很简单的一个统计. 现在date已经规整过, 只需要下面的语句就能得出某年的主题数

    df[df['date'] > '2015'][df['date'] < '2016'].count()

    之后新建一个dataframe, 再plot出来, 就是需要的统计图.

    df_count_year = DataFrame([df[df['date'] > '2015'][df['date'] < '2016']['title'].count(), df[df['date'] > '2016'][df['date'] < '2017']['title'].count()], index = [2015, 2016])
    

    这里写图片描述

    每年发主题最多的用户

    这个统计也不难. 只需要计算当年用户id出现次数就可以. 跟上一个没区别, 只是多了一个判定, 就不举例了.

    每年被回复最多的用户

    这个分析使用到了分组和排序, 因为需要计算times的总和.

    df[df['date'] > '2014'][df['date'] < '2015'].groupby(df['author']).sum().sort_values('times')
    

    首先取到某一年的数据, 再以author进行分组, 计算各行的总和, 再以times进行排序, 得到了需要的数据.

    这里写图片描述

    每个月的主题数

    这个最像数据分析, 使用了pandas的resample(‘M’). 感兴趣可以自己了解下, 这里直接给出结果.

    这里写图片描述

    总结

    • json转换的时候出了很多次编码问题, 需要注意
    • 爬数据用了八个小时有余. 到最后看着python内存占用越来越大, 真怕出点什么意外导致本地化失败. 也说明了爬虫并不合理, 应该分段进行储存. 主要也没想到会爬这么多数据.
    • ipython对于中文并不算友好, 尤其用中文当key去检索数据的时候, 非常麻烦, 最好想个方法解决.
    • matplotlib对中文支持有限, 需要自己去修改一些配置.
    展开全文
  • scrapy提取数据

    2019-09-29 06:03:25
    Scrapy中的Selector类是基于lxml库建立的,并且简化了API接口。在使用的过程中先使用xpath或者css选择器选中页面中要提取的数据,然后进行提取。 从页面中提取数据的核心技术是HTTP文本解析,在python中常用的模块...
  • 最后用Pandas进行了分析,并给出了数据可视化。 准备工作 乐有家长沙二手房信息网页(https://changsha.leyoujia.com/esf/) 接着用Scrapy shell验证二手房XPath表达式 #标题 response.xpath('./div[@class=&...
  • Python数据分析:爬虫框架scrapy基础知识点 scrapy简介: 开源的爬虫框架 快速强大,只需编写少量代码即可完成爬取任务 易扩展,添加新的功能模块 scrapy抓取过程: 使用start_urls作为初始url生成Request,默认...
  • 使用scrapy框架爬取51job全国数据分析职位信息并做简单分析 工具:scrapy,MongoDB,Excel,tableau 1.分析网页链接,里面包含有【keyword=数据分析师&keywordtype=2&curr_page=1】这些关键信息,可以看出...
  • scrapy架构设计分析

    2017-05-15 14:13:00
    scrapy是一个Python爬虫框架。我们自己用requests也能写爬虫(GET某个URL,然后Parse网页的内容),那么,问题来了,scrapy高明在哪些地方呢?...(1) 最重要的模块是Engine:它是数据流的指挥官,负责控制数据流(...
  • 下面我们就一起看看python爬虫中scrapy处理项目数据的方法吧。 1、拉取项目 $ git clone https://github.com/jonbakerfish/TweetScraper.git $ cd TweetScraper/ $ pip install -r requirements.txt #add '--user' ...
  • 本文建立在学习完大壮老师视频Python最火爬虫框架Scrapy入门实践,自己一步一步操作后做一个记录(建议跟我一样的新手都一步一步进行操作). 主要介绍: 1、scrapy框架简介、数据在框架内如何进行流动 2、scrapy框架...
  • Scrapy爬虫框架介绍 框架简介 是基于python实现爬虫的重要技术路线。scrapy不是一个函数功能库,而是一个爬虫框架。 包括7个部分 :5+2结构 5个主题部分,2个中间键。...Spider ENGINE ENGINE...
  • 本篇介绍一个scrapy的实战爬虫项目,并对爬取信息进行简单的数据分析。目标是北京二手房信息,下面开始分析。网页结构分析采用安居客网页信息作为二手房的信息来源,直接点击进入二手房信息的页面。每页的住房信息:...
  • 问题描述:用Scrapy来爬取某论坛数据,在数据导入mysql数据库中,一直有个别数据重复插入。 修改之前代码: class AyncMysqlPipeline(object): # 初始化数据库连接 def __init__(self): dbparms = dict( host...
  • Scrapy爬虫数据架构图

    2019-10-29 22:52:21
    Scrapy是基于用Python写...Scrapy中的数据流由执行引擎控制,如下所示: Engine获得从爬行器中爬行的初始请求。 Engine在调度程序中调度请求,并请求下一次抓取请求。 调度程序将下一个请求返回到引擎。 引擎将请...
  • Scrapy数据写入Elasticsearch

    千次阅读 热门讨论 2018-03-19 15:03:16
    Scrapy数据写入到Elsaticsearch 安装Elasticsearch 这里我们安装的是elasticsearch-rtf (elasticsearch中文发行版,针对中文集成了相关插件,方便新手学习测试。) 这里是github上的链接,可以使用git...
  • 按照我的理解,数据分析大概整体分为5大模块——数据收集、数据清洗、数据挖掘、数据建模、数据应用。 今天,我便“开车”进军第一大模块!数据收集!!!! 数据收集,通俗一点即爬虫技术,即利用脚本模拟浏览器...
  • scrapy介绍 Scrapy 是一套基于Twisted、纯python实现的异步爬虫框架,用户...引擎负责控制数据流在系统所有组件中的流动,并在相应动作发生时触发事件,是框架的核心。 Scheduler(调度器) 调度器从引擎接受reques...
  • :star: 数据分析结果: PM2.5中的污染水平: :link: 更多详细信息: : :star: 细节: :round_pushpin: 网站: : :round_pushpin: 代码语言: Python3 :round_pushpin: 刮板:刮板 :round_pushpin: 图书...
  • 安装好scrapy,pycharm,conda等等 进入指定环境(激活环境),安装scrapy,可用 conda 或者 pip 命令 1. 指定项目目录 cd D:\Python\scrapy 2. 检查环境(或者自己新建环境,并激活,参照命令) conda env list 3. ...
  • 京东全网手机爬虫scrapy_redis及决策树数据分析 参考了实验楼并进行延伸 SqlServer版本:13.0.1742.0 (也可使用mongodb) Python版本3.7.1 软件:Pycharm , Navicat , SSMS,RedisDesktopManager 项目架构: 爬虫...
  • 本篇介绍一个scrapy的实战爬虫项目,并对爬取信息进行简单的数据分析。目标是北京二手房信息,下面开始分析。网页结构分析采用安居客网页信息作为二手房的信息来源,直接点击进入二手房信息的页...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,220
精华内容 6,888
关键字:

scrapy与数据分析