网页抓取数据百度百科(【干货】如何选择合适的方式爬取数据?(一))

优采云 发布时间: 2021-10-21 12:13

  网页抓取数据百度百科(【干货】如何选择合适的方式爬取数据?(一))

  大家好,这是我第一次写这样的分享项目文章。可能很水,不完整,肯定有一些错误。希望大家在评论中指点迷津。谢谢!

  一、前言

  网络爬虫(又称网络蜘蛛、网络机器人)是按照一定的规则自动抓取万维网上信息的程序或脚本。其他不太常用的名称是蚂蚁、自动索引、模拟器或蠕虫。------百度百科

  用人类的话说,爬虫是用来定时获取海量数据,然后进行处理和使用的。是大数据、金融、机器学习等领域的必要支撑条件之一。

  目前在一线城市,爬虫的薪资待遇都比较客观。后来晋升中高级爬虫工程师、数据分析师、大数据开发岗位都是不错的过渡。

  二、项目目标

  其实这里介绍的项目不需要太复杂。最终目的是将帖子的每条评论爬取到数据库中,并进行数据更新、防止重复爬取、反爬取等措施。

  三、项目准备

  这部分主要介绍本文用到的工具、涉及的库、网页等信息。

  软件:PyCharm

  所需的库:Scrapy、selenium、pymongo、user_agent、datetime

  目标 网站:

  http://bbs.foodmate.net

  插件:chromedriver(版本必须正确)

  四、项目分析1、确定爬取的结构网站

  简而言之:确定网站的加载方式,如何正确进入post逐级抓取数据,使用什么格式保存数据等。

  其次,观察网站的层次结构,也就是如何按照版块一点一点的进入post页面。这对于这个爬虫任务非常重要,也是编写代码的主要部分。

  2、如何选择正确的方式抓取数据?

  目前我知道的爬虫方法大概有以下几种(不全,但比较常用):

  1)请求框架:使用这个http库灵活抓取需要的数据。简单但过程有点繁琐,可以配合抓包工具来获取数据。但是需要确定headers和对应的请求参数,否则无法获取数据;很多app爬取,图片视频爬取,爬取停止,比较轻巧灵活,高并发分布式部署也很灵活,功能可以更好的实现。

  2)Scrapy框架:scrapy框架可以说是爬虫最常用、最好的爬虫框架。它有很多优点:scrapy 是异步的;它采用更具可读性的 xpath 而不是常规的;强大的统计和日志系统;同时抓取不同的 URL;支持shell模式,方便独立调试;支持编写中间件,方便编写一些统一的过滤器;它可以通过管道存储在数据库中,等等。这也是本次要介绍的框架(结合selenium库)文章。

  五、项目实现

  1、第一步:确定网站的类型

  先说明什么意思,看什么网站,先看网站的加载方式,是静态加载,动态加载(js加载),还是其他方式;根据不同的加载方式需要不同的方法。然后我们观察了今天爬取的网站,发现这是一个按时间顺序排列的论坛。首先猜测是静态加载网站;我们打开了组织js加载的插件,如下图。

  

  刷新后发现确实是静态的网站(如果能正常加载,基本就是静态加载了)。

  2、步骤二:确定层级关系

  其次,今天我们要爬取的网站是美食论坛网站,它是静态加载的网站,在前面的分析中我们已经了解了,然后是层次结构:

  

  大概就是上面这个过程,一共三个层次的渐进访问,然后到达post页面,如下图。

  

  很多人学习python,不知道从哪里开始。

  很多人学习python,掌握了基本语法后,不知道从哪里找案例上手。

  许多做过案例研究的人不知道如何学习更高级的知识。

  所以对于这三类人,我会为大家提供一个很好的学习平台,免费领取视频教程、电子书、课程源码!

  QQ群:721195303

  部分代码显示:

  一级接口:

  def parse(self, response):

self.logger.info("已进入网页!")

self.logger.info("正在获取版块列表!")

column_path_list = response.css('#ct > div.mn > div:nth-child(2) > div')[:-1]

for column_path in column_path_list:

col_paths = column_path.css('div > table > tbody > tr > td > div > a').xpath('@href').extract()

for path in col_paths:

block_url = response.urljoin(path)

yield scrapy.Request(

url=block_url,

callback=self.get_next_path,

)

  次要接口:

  def get_next_path(self, response):

self.logger.info("已进入版块!")

self.logger.info("正在获取文章列表!")

if response.url == 'http://www.foodmate.net/know/':

pass

else:

try:

nums = response.css('#fd_page_bottom > div > label > span::text').extract_first().split(' ')[-2]

except:

nums = 1

for num in range(1, int(nums) + 1):

tbody_list = response.css('#threadlisttableid > tbody')

for tbody in tbody_list:

if 'normalthread' in str(tbody):

item = LunTanItem()

item['article_url'] = response.urljoin(

tbody.css('* > tr > th > a.s.xst').xpath('@href').extract_first())

item['type'] = response.css(

'#ct > div > div.bm.bml.pbn > div.bm_h.cl > h1 > a::text').extract_first()

item['title'] = tbody.css('* > tr > th > a.s.xst::text').extract_first()

item['spider_type'] = "论坛"

item['source'] = "食品论坛"

if item['article_url'] != 'http://bbs.foodmate.net/':

yield scrapy.Request(

url=item['article_url'],

callback=self.get_data,

meta={'item': item, 'content_info': []}

)

try:

callback_url = response.css('#fd_page_bottom > div > a.nxt').xpath('@href').extract_first()

callback_url = response.urljoin(callback_url)

yield scrapy.Request(

url=callback_url,

callback=self.get_next_path,

)

except IndexError:

pass

  三级接口:

  def get_data(self, response):

self.logger.info("正在爬取论坛数据!")

item = response.meta['item']

content_list = []

divs = response.xpath('//*[@id="postlist"]/div')

user_name = response.css('div > div.pi > div:nth-child(1) > a::text').extract()

publish_time = response.css('div.authi > em::text').extract()

floor = divs.css('* strong> a> em::text').extract()

s_id = divs.xpath('@id').extract()

for i in range(len(divs) - 1):

content = ''

try:

strong = response.css('#postmessage_' + s_id[i].split('_')[-1] + '').xpath('string(.)').extract()

for s in strong:

content += s.split(';')[-1].lstrip('\r\n')

datas = dict(content=content, # 内容

reply_id=0, # 回复的楼层,默认0

user_name=user_name[i], # ⽤户名

publish_time=publish_time[i].split('于 ')[-1], # %Y-%m-%d %H:%M:%S'

id='#' + floor[i], # 楼层

)

content_list.append(datas)

except IndexError:

pass

item['content_info'] = response.meta['content_info']

item['scrawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

item['content_info'] += content_list

data_url = response.css('#ct > div.pgbtn > a').xpath('@href').extract_first()

if data_url != None:

data_url = response.urljoin(data_url)

yield scrapy.Request(

url=data_url,

callback=self.get_data,

meta={'item': item, 'content_info': item['content_info']}

)

else:

item['scrawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

self.logger.info("正在存储!")

print('储存成功')

yield item

  3、第三步:确定爬取方式

  由于是静态网页,我首先决定使用scrapy框架直接获取数据,通过初步测试,发现该方法确实可行。爬虫限制了爬取速度,导致我被网站限制,网站从静态加载的网页改成:进入网页前动态加载的网页验证算法,直接访问会被拒绝背景。

  但是这种问题怎么会是我的小聪明呢?经过短暂的思考(1天),我将方案改为scrapy框架+selenium库的方法,通过调用chromedriver,模拟访问网站等网站加载后,爬取没有完成。后续证明,该方法确实可行且有效。

  代码的实现部分如下:

  def process_request(self, request, spider):

chrome_options = Options()

chrome_options.add_argument('--headless') # 使用无头谷歌浏览器模式

chrome_options.add_argument('--disable-gpu')

chrome_options.add_argument('--no-sandbox')

# 指定谷歌浏览器路径

self.driver = webdriver.Chrome(chrome_options=chrome_options,

executable_path='E:/pycharm/workspace/爬虫/scrapy/chromedriver')

if request.url != 'http://bbs.foodmate.net/':

self.driver.get(request.url)

html = self.driver.page_source

time.sleep(1)

self.driver.quit()

return scrapy.http.HtmlResponse(url=request.url, body=html.encode('utf-8'), encoding='utf-8',

request=request)

  4、第四步:确定爬取数据的存储格式

  这部分不用说了,根据自己的需要,在items.py中设置需要爬取的数据格式。只需使用此格式保存在项目中:

  class LunTanItem(scrapy.Item):

"""

论坛字段

"""

title = Field() # str: 字符类型 | 论坛标题

content_info = Field() # str: list类型 | 类型list: [LunTanContentInfoItem1, LunTanContentInfoItem2]

article_url = Field() # str: url | 文章链接

scrawl_time = Field() # str: 时间格式 参照如下格式 2019-08-01 10:20:00 | 数据爬取时间

source = Field() # str: 字符类型 | 论坛名称 eg: 未名BBS, 水木社区, 天涯论坛

type = Field() # str: 字符类型 | 板块类型 eg: '财经', '体育', '社会'

spider_type = Field() # str: forum | 只能写 'forum'

  5、第五步:确认保存数据库

  本项目选用的数据库是 mongodb。因为是非关系型数据库,优势很明显,格式要求也没有那么高。可灵活存储多维数据。一般是爬虫首选的数据库(别跟我说redis,知道的我会用,主要是不会)

  代码:

  import pymongo

class FMPipeline():

def __init__(self):

super(FMPipeline, self).__init__()

# client = pymongo.MongoClient('139.217.92.75')

client = pymongo.MongoClient('localhost')

db = client.scrapy_FM

self.collection = db.FM

def process_item(self, item, spider):

query = {

'article_url': item['article_url']

}

self.collection.update_one(query, {"$set": dict(item)}, upsert=True)

return item

  这时候,有聪明的朋友会问:同一个数据爬两次怎么办?(换句话说,就是重复检查功能)

  我之前没想过这个问题。后来问了大佬才知道。这是我们保存数据的时候做的,就这句话:

  query = {

'article_url': item['article_url']

}

self.collection.update_one(query, {"$set": dict(item)}, upsert=True)

  通过帖子的链接判断是否有重复数据爬取。如果重复数据可以理解为覆盖它,那么数据也可以被更新。

  6、其他设置

  多线程、头、管道传输顺序等问题,都在settings.py文件中设置。详情请参考编辑器的项目查看。我不会在这里详细介绍。

  七、效果展示

  1、 点击运行,控制台会显示结果,如下图所示。

  

  

  2、 中途将很多帖子的爬取任务堆到队列中,再进行多线程处理。我设置了16个线程,速度还是很可观的。

  

  3、数据库显示:

  

  content_info 存储了每个帖子的所有评论以及相关用户的公开信息。

  八、总结

  1、本文文章主要介绍食物网站数据采集和存储过程,并详细讲解如何分析网页结构、爬取策略和网站类型。,层级关系,爬取方式和数据存储过程,最后实现将帖子的每条评论都爬进数据库,并且可以更新数据,防止重复爬取,反爬取等,干货满满.

  2、 总的来说,这个项目并不是特别难。只要思路正确,找到数据规律,可以说是轻而易举。我想只是我之前没有完全走完这个过程。我希望它会对你有所帮助。这将是我最大的荣幸。

  3、遇到问题,首先想到的不是问同事、朋友、老师,而是去谷歌、百度,看看有没有类似的情况,看看别人的经历,而你一定要学会自己发现问题、思考问题、解决问题。,这对以后的工作很有帮助(之前有人说我还没有离开我的学生时代,就是我喜欢问同事)。在网上查了一些资料,还是没有头绪,再问别人,别人会做的我更愿意帮你~

  还是要推荐一下自己搭建的Python学习群:721195303,群里都是学Python的。如果您想学习或正在学习Python,欢迎您加入。大家是软件开发党,不定期分享干货(仅限Python软件开发相关),包括2021年自己编译的最新Python进阶资料和零基础教学。欢迎进阶有兴趣的朋友在 Python 中加入!

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线