「Python爬虫系列讲解」十、基于数据库存储的 Selenium 博客爬虫

本专栏是以杨秀璋老师爬虫著作《Python网络数据爬取及分析「从入门到精通」》为主线、个人学习理解为主要内容,以学习笔记形式编写的。
本专栏不光是自己的一个学习分享,也希望能给您普及一些关于爬虫的相关知识以及提供一些微不足道的爬虫思路。
专栏地址:Python网络数据爬取及分析「从入门到精通」
更多爬虫实例详见专栏:Python爬虫牛刀小试

 前文回顾:
「Python爬虫系列讲解」一、网络数据爬取概述
「Python爬虫系列讲解」二、Python知识初学 
「Python爬虫系列讲解」三、正则表达式爬虫之牛刀小试 
「Python爬虫系列讲解」四、BeautifulSoup 技术
「Python爬虫系列讲解」五、用 BeautifulSoup 爬取电影信息
「Python爬虫系列讲解」六、Python 数据库知识
「Python爬虫系列讲解」七、基于数据库存储的 BeautifulSoup 招聘爬取
「Python爬虫系列讲解」八、Selenium 技术
「Python爬虫系列讲解」九、用 Selenium 爬取在线百科知识


 目录

1 博客网站

2 Selenium 爬取博客信息

         2.1 Forbidden 错误

2.2 分析博客网站翻页方法

2.3 DOM 树节点分析及网页爬取

3 MySQL 数据库存储博客信息

3.1 Navicat for MySQL 创建表

3.2 Python 操作 MySQL 数据库

3.3 代码实现

4 本文小结


本文将讲述一个基于数据库存储的 Selenium Python 爬虫,用于爬取某博客网站的博客信息,包括博客标题、摘要、远度量、评论量和作者等,并存储至本地数据库,从而能能够灵活地为用户提供所需数据,同时也为人类博客行为模型、热点话题等提供强有力的支撑。

1 博客网站

博客(Blogger),为Web Log的混成词。它的正式名称为网络日记;是使用特定的软件,在网络上出版、发表和张贴个人文章的人,或者是一种通常由个人管理、不定期张贴新的文章的网站。博客上的文章通常以网页形式出现,并根据张贴时间,以倒序排列。通常具备RSS订阅功能。博客是继MSN、BBS、ICQ之后出现的第4种网络交流方式,现已受到大家的欢迎,是网络时代的个人“读者文摘”,是以超级链接为入口的网络日记,它代表着新的生活、工作和学习方式。许多博客专注在特定的课题上提供评论或新闻,其他则被作为个人性的日记。一个典型的博客结合了文字、图像、其他博客或网站的链接及其它与主题相关的媒体,能够让读者以互动的方式留下意见,是许多博客的重要要素。大部分的博客内容以文字为主,但仍有一些博客专注在艺术、摄影、视频、音乐、博客等各种主题。博客是社会媒体网络的一部分。比较著名的有新浪、CSDN、博客园等。

博客作为 Web 2.0 的重要产物,给网络和用户带来了很多便利,其主要功能及特点有:

  • 网络日志:这时博客最早、最基本的功能,就是发表个人网络日志。
  • 个人文集:把自己写的文章按照一定的时间顺序、目录或者标签发表到自己的博客上。
  • 个性展示:博客时完全以个人为中心的展示,每个人的博客都是不同的,从博客中可以看出每个人的个性。
  • 结交博友:通过博客及博客文章可以结交到很多志同道合的博友。
  • 提高个人影响力:博客是一个很好地自我展示和互动交流的平台,通过这个平台可以在博友之间提高自己的影响力。

同时,博客会产生各种丰富的数据集,这些数据集将广泛应用于科研工作中。 本文将主要介绍如何利用 Selenium 来爬取技术类博客数据。

2 Selenium 爬取博客信息

与前文中讲过的调用 BeautifulSoup 扩展库爬取招聘网站类似,调用 Selenium 扩展库爬取博客网站的核心步骤如下:

  1. 分析网页超链接的搜索规则,并探索分页查找的跳转方法;
  2. 分析网页 DOM 树结构,确定 Selenium 定位所需信息的代码;
  3. 调用 Navicat for MySQL 工具操作数据库,包括创建数据库、创建表等;
  4. 编写 Python 操作 MySQL 数据库的代码,将数据存储至本地。

2.1 Forbidden 错误

以作者自己的博客为例,首先打开博客地址:https://blog.csdn.net/IT_charge,可以看到许多博客信息,他们的布局有一定的规律,比如标题在第一行,摘要在中间,时间、浏览量和评论数在左下角。

如果采用前面介绍的 BeautifulSoup 技术进行定位爬取,则会提示“HTTPError:Forbidden”错误,这是常见的被服务器识别了爬虫程序而拒绝访问的 403 错误。是不是就没有办法解决呢?答案当然是否定的,这是我们可以通过 F12 键获取该浏览器对应的 user-agent 参数信息,作为消息头给爬虫程序加上就完美币避免了“403”问题。在加上消息头之后,网站就会捕获到该消息头信息,会认为此次访问使用户正常的浏览操作,从而反馈数据。

值得注意的是,消息头通常使用字典形式以键值对的方式进行传入。

下面给出添加消息头后的爬虫框架(该代码实现的是爬取王者荣耀官网全英雄图片):

import requests
import pprint
import time

star_time = time.time() # 时间戳

# 步骤:
# 1.确定做正确url(分析当前url地址构成)
base_url = 'https://pvp.qq.com/web201605/js/herolist.json'
headers =  {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'
}

# 2.请求数据(大范围数据)
response = requests.get(url=base_url, headers=headers)
data_list = response.json()
# gbk(windows系统默认编码方式) utf-8(谷歌浏览器编码方式)
# get(明文请求) post(加密请求)
# pprint.pprint(data_list)

# 3.数据解析
for data in data_list:
    # print(data)
    cname = data['cname']  # 英雄名字
    ename = data['ename']  # 英雄id值
    try:
        skin_name = data['skin_name'].split('|')  # 皮肤名字(数量)
    except Exception as e:
        print(e)
    # print(cname, ename, skin_name)
    # 构建皮肤数量的循环
    for skin_num in range(1, len(skin_name) + 1):
        # http://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/109/109-bigskin-7.jpg
        # http://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/ + 英雄id + / + 英雄id-bigskin-皮肤序号+.jpg
        skin_url = 'http://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/' + str(ename) + '/' + str(ename) + '-bigskin-' + str(skin_num) + '.jpg'
        # print(skin_url)
        skin_data = requests.get(url=skin_url, headers=headers).content # 提取二进制数据

        # 4.数据保存(本地文件、数据库)
        with open('F:/img/' + cname + '-' + skin_name[skin_num - 1] + '.jpg', mode='wb') as f:
            print('正在下载皮肤:', cname + '-' + skin_name[skin_num - 1])
            f.write(skin_data)

end_time = time.time()
print('花费时间(秒):', int(end_time - star_time))
print('已经全部爬取完毕!')

但对于本文而言,是使用 Selenium 技术实现爬取内容时,能够模拟浏览器,就像真实用户一样操作浏览器,从而“欺骗”网站服务器,实现定位和爬取相关网页。 

2.2 分析博客网站翻页方法

访问博客地址:https://blog.csdn.net/IT_charge/article/list/2,可以看到很多条博客信息,如下图us哦好四,网页底部显示了页码的超链接。

可以获取到,博主博客总页码数 “3”,可以以此再定义一个循环分别爬取每页下的所有博客信息。

首先查看对应位置的 HTML 源码。

然后在使用正则表达式获取该字符串的第二个数字,用以获取博主页码总数。

driver.get('https://blog.csdn.net/IT_charge')
texts = driver.find_element_by_xpath('//*[@id="Paging_0011132059102831215"]/ul/li').text
m = re.findall(r'(w*[0-9]+)\w*', texts)
print('页数:', + str(m[1]))
# 页数:3

在切换页面是,相信大家已经看出来其 URL 的变化规律

 我们通过这个规律采用 URL 拼接的方法实现翻页功能。

2.3 DOM 树节点分析及网页爬取

在浏览器选中某篇博客信息,查看其对应的 URL 源码发现,每篇文章都是由 <div>和</div> 组成的。

比如获取某篇文章的标题:

driver.get('https://blog.csdn.net/IT_charge/article/list/2')
texts = driver.find_element_by_xpath('//*[@id="mainBox"]/main/div[2]/div[1]/h4/a').text
print(texts)

再比如获取某篇文章的摘要部分:

driver.get('https://blog.csdn.net/IT_charge/article/list/2')
texts = driver.find_element_by_xpath('//*[@id="mainBox"]/main/div[2]/div[1]/p/a').text
print(texts)

对于获取一些细节之处的信息时,可能会用到正则表达式。

比如通过 URL 获取博主的 id 值:

url = 'https://blog.csdn.net/IT_charge'
print(url.split('/')[-1])

比如获取阅读数,浏览数、评论数等:

# 获取数字
name = '2020-05-19 10:40:33 阅读(13045) 评论(134)'
print(name)
mode = re.compile(r'\d+\.?\d*')
# 输出列表
print(mode.findall(name))
# 输出阅读数
print(mode.findall(name)[-2])
# 输出时间
end = name.find(r' 阅读')
print(name[:end])
# 标准化日期和时间
a = time.struct_time(time.strptime(name[:end], '%Y-%m-%d %H:%M:%S'))
print(a)

3 MySQL 数据库存储博客信息

数据库方面主要利用 MySQL 数据库本地创建一张表,该表用于存储博客信息。结合前面分析的,该表应该主要包括以下字段:序号、博客标题、摘要、发布时间、阅读数、评论数、博客超链接、博客作者、点赞数、其他。

3.1 Navicat for MySQL 创建表

创建数据库及表,命名为csdn。

3.2 Python 操作 MySQL 数据库

# 博客标题
                article_title = driver.find_elements_by_xpath('//*[@id="mainBox"]/main/div[2]/div[{}]/h4/a'.format(i))
                Artitle = article_title[0].text

                # 博客摘要
                artical_description = driver.find_elements_by_xpath('//*[@id="mainBox"]/main/div[2]/div[{}]/p/a'.format(i))
                Description = artical_description[0].text

                print(Artitle)
                print(Description)

                # 获取博客作者
                Author = url.split('/')[-1]
                # 获取阅读、评论数
                artical_YDNum = driver.find_elements_by_xpath('//*[@id="mainBox"]/main/div[2]/div[{}]/div[1]/p/span[2]'.format(i))
                artical_PLNum = driver.find_elements_by_xpath('//*[@id="mainBox"]/main/div[2]/div[{}]/div[1]/p/span[3]'.format(i))
                YDNum = artical_YDNum[0].text
                PLNum = artical_PLNum[0].text
                print(YDNum)
                print(PLNum)
                # 获取发布时间
                artical_FBTime = texts = driver.find_elements_by_xpath('//*[@id="mainBox"]/main/div[2]/div[{}]/div[1]/p/span[1]'.format(i))
                FBTime = artical_FBTime[0].text
                print(FBTime)

3.3 代码实现

import os
import re
import time, datetime
import codecs
import MySQLdb
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import selenium.webdriver.support.ui as ui

# 打开 Chrome 浏览器,这顶等待加载时间
chromedriver = 'E:/software/chromedriver_win32/chromedriver.exe'
os.environ["webdriver.chrome.driver"] = chromedriver
driver = webdriver.Chrome(chromedriver)
wait = ui.WebDriverWait(driver, 10)

#主函数
def main():
    url = 'https://blog.csdn.net/IT_charge'
    allPage = 2
    print('页码总数:', allPage)
    time.sleep(1)

    # 数据库操作结合
    try:
        # 连接数据库
        conn = MySQLdb.connect(host='localhost', user='root', passwd='123456', port=3306, db='csdn',charset='utf8')
        # cursor()函数定义游标
        cur = conn.cursor()

        # 设置中文编码方式
        conn.set_character_set('utf8')
        cur.execute('SET NAMES utf8;')
        cur.execute('SET CHARACTER SET utf8')
        cur.execute('SET character_set_connection=utf8')

        # 爬取具体内容
        m = 1
        ID = 1
        while m <= allPage:       # 总页码
            ur = url + '/article/list/' + str(m)
            print(ur)
            driver.get(ur)
            time.sleep(3)

            for i in range(1, 30):
                # 插入数据
                sql = 'insert into csdn(ID,URL,Author,Artitle,Description,Manage,FBTime,YDNum,PLNum,DZNum)values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
                # 博客标题
                article_title = driver.find_elements_by_xpath('//*[@id="mainBox"]/main/div[2]/div[{}]/h4/a'.format(i))
                Artitle = article_title[0].text

                # 博客摘要
                artical_description = driver.find_elements_by_xpath('//*[@id="mainBox"]/main/div[2]/div[{}]/p/a'.format(i))
                Description = artical_description[0].text

                print(Artitle)
                print(Description)

                # 获取博客作者
                Author = url.split('/')[-1]
                # 获取阅读、评论数
                artical_YDNum = driver.find_elements_by_xpath('//*[@id="mainBox"]/main/div[2]/div[{}]/div[1]/p/span[2]'.format(i))
                artical_PLNum = driver.find_elements_by_xpath('//*[@id="mainBox"]/main/div[2]/div[{}]/div[1]/p/span[3]'.format(i))
                YDNum = artical_YDNum[0].text
                PLNum = artical_PLNum[0].text
                print(YDNum)
                print(PLNum)
                # 获取发布时间
                artical_FBTime = texts = driver.find_elements_by_xpath('//*[@id="mainBox"]/main/div[2]/div[{}]/div[1]/p/span[1]'.format(i))
                FBTime = artical_FBTime[0].text
                print(FBTime)

                cur.execute(sql, (ID,url,Author,Artitle,Description,0,FBTime,YDNum,PLNum,0))
                ID = ID + 1
            else:
                print('数据库插入成功!')
            m = m + 1

    except MySQLdb.Error as e:
        print('MySQL Error %d: %s' % (e.args[0], e.args[1]))
    finally:
        cur.close()
        conn.commit()
        conn.close()

if __name__ == '__main__':
    main()

4 本文小结

网络爬虫是使用技术手段批量获取网站信息的一种方法,而网络反爬虫是使用一定技术手段阻止爬虫批量获取网站信息的方法。在爬取数据时往往会遇到各种各样的拦截,比如常见的 “403 Forbidden” 错误,它标识服务器已经识别出爬虫并拒绝处理用户的请求。当使用 BeautifulSoup 技术爬取博客时,得到的反馈就是 “HTTPError:Forbidden” 错误,此时可以在爬虫代码中添加 Headers 的 User-Agent 值来实现正常抓取;而本文使用的另一种方法,通过 Selenium 技术调用 Chrome 浏览器来实现网站爬取,并将爬取的数据存储至 MySQL 数据库中。同时,当同一网站短时间内被访问多次或同一账号短时间内进行多次相同的操作时,也常常会被网站反爬虫拦截,比如微博、淘宝等。这时可以通过 IP代理或 PhantomJS 解决,他们都是破解反爬虫的利器。


欢迎留言,一起学习交流~

感谢阅读

END

  • 18
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

荣仔!最靓的仔!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值