编程笔记

编程笔记

【Python】爬虫保姆级教程(五)(数据存储:MySQL数据库、Python与MySQL交互操作)
2025-01-29

文章目录

  • 介绍
    • 爬虫的工作原理
    • 爬虫的主要类型
    • 构建爬虫的技术栈
    • 爬虫的最佳实践
    • 面临的挑战
  • MySQL数据库
    • 基本操作
    • 数据类型
    • 常用的SQL语句
      • 数据定义语言
      • 数据操作语言
      • 数据查询语言
  • Python与MySQL的交互
    • 插入数据步骤
    • 批量插入数据
    • 查询操作步骤
    • 更新与删除数据
    • 案例1
    • 案例2
    • 案例3
    • 案例4
    • 案例5

个人主页:道友老李

欢迎加入社区:道友老李的学习社区

介绍

爬虫(Web Crawler 或 Web Spider)是一种自动化程序或脚本,它通过互联网上的链接从一个网页到另一个网页地抓取数据。爬虫通常用于搜索引擎索引、数据分析、内容聚合等目的。它们按照一定的规则遍历网页,下载页面内容,并将这些信息存储起来以供后续处理。

爬虫的工作原理

  1. 种子 URL:爬虫从一组初始的 URL(称为“种子”)开始。
  2. 获取页面:访问每个种子 URL,下载对应的网页内容。
  3. 解析页面:分析网页的 HTML 结构,提取有用的数据和所有指向其他页面的链接。
  4. 更新队列:将新发现的链接添加到待访问列表中,确保不会重复抓取已经访问过的页面。
  5. 存储数据:保存所提取的数据到数据库或其他形式的持久化存储中。
  6. 遵守规范:遵循网站的 robots.txt 文件和其他相关协议,尊重网站设定的爬行限制。

爬虫的主要类型

  • 通用爬虫(General Purpose Crawlers):如谷歌、百度等搜索引擎使用的爬虫,目的是尽可能广泛地收集互联网上的信息,构建大型索引库。
  • 聚焦爬虫(Focused Crawlers):专注于特定主题或领域的网页,比如只抓取与科技新闻相关的网站。
  • 增量式爬虫(Incremental Crawlers):定期重新抓取已知页面,以捕捉任何更新或变化的内容。
  • 深度优先搜索爬虫(DFS Crawlers):尽可能深地沿着一条路径前进,直到无法继续为止,然后回溯并选择另一条路径。
  • 广度优先搜索爬虫(BFS Crawlers):先抓取同一层级的所有页面,再向下一层级扩展。

构建爬虫的技术栈

  • 编程语言:Python 是最常用的语言之一,因为它有丰富的库支持,如 Scrapy、BeautifulSoup、Requests 等;此外,Java、JavaScript(Node.js)、Ruby 也有相应的框架。
  • HTTP 请求库:例如 Python 的 Requests 库,用于发送 HTTP 请求并接收响应。
  • HTML 解析器:如 BeautifulSoup、lxml,用于解析 HTML 文档结构,提取所需信息。
  • 异步 I/O 和多线程:为了提高效率,可以使用异步 I/O 模型(如 asyncio)或多线程技术来并发处理多个请求。
  • 数据库/文件系统:用来存储抓取下来的数据,可以选择关系型数据库(MySQL、PostgreSQL)、NoSQL 数据库(MongoDB)或者直接写入文件系统。
  • 代理服务和 IP 轮换:为了避免被目标网站封禁 IP 地址,可以通过代理服务器分散请求来源,甚至动态更换 IP。
  • 浏览器模拟工具:对于需要执行 JavaScript 渲染才能显示内容的网站,可以使用 Selenium、Puppeteer 等工具来模拟真实用户的浏览器行为。

爬虫的最佳实践

  • 遵守法律和道德准则:确保你的爬虫活动符合法律法规,并且不侵犯个人隐私或版权。
  • 尊重网站规则:检查并遵守目标网站的 robots.txt 文件中的指示,不要对服务器造成过大的负载。
  • 设置合理的抓取频率:避免过于频繁地请求同一个站点,以免影响其正常运作。
  • 处理异常情况:为网络故障、超时等问题设计好容错机制。
  • 数据清洗和验证:确保抓取到的数据是准确无误的,并对其进行必要的清理和转换。

面临的挑战

  • 反爬虫措施:许多网站会采取各种手段防止被恶意爬虫滥用,如验证码、登录验证、IP 黑名单等。
  • 动态内容加载:随着 AJAX 和前端框架的发展,越来越多的网站采用异步加载方式,这增加了爬虫抓取完整页面内容的难度。
  • 数据一致性:在分布式环境中,保证不同节点之间数据的一致性和准确性是一个复杂的问题。

总之,构建一个高效的爬虫需要综合考虑技术实现、性能优化以及伦理问题等多个方面。正确地设计和部署爬虫可以帮助我们更好地理解和利用互联网上的海量信息资源。

MySQL数据库

  • MySQL是一种关系数据库管理系统,是一种开源软件
  • 由瑞典MySQL AB公司开发,2008年1月16号被Sun公司收购。2009年,SUN又被Oracle收购
  • MySQL软件采用双授权政策,分为社区版和商业版。由于体积小、速度快、总体拥有成本低,尤其是开放源码特点,一般中小型网站的开发都选择MySQL作为网站数据库。
  • 由于其社区版的性能卓越,搭配PHP和Apache可组成良好的开发环境。
  • MySQL能够工作在众多不同的平台上

基本操作

  • 启动服务:net start mysql80
  • 登录服务:mysql -h127.0.0.1 -uroot -proot -P3306
  • 关闭服务:net stop mysql80

数据类型


MySQL支持选择在该类型关键字后面的括号内指定整数值的显示宽度(例如,INT(4))。显示宽度并不限制可以在列内保存的值的范围,也不限制超过列的指定宽度的值的显示

常用的SQL语句

  • SQL语言包含4个部分
    • 数据定义语言(如create,drop,alter等语句)
    • 数据查询语言(select语句)
    • 数据操纵语言(insert,delete,update语句)
    • 数据控制语言(如grant,revoke,commit,rollback等语句)
    • 数据操纵语言针对表中的数据,而数据定义语言针对数据库或表

数据定义语言


数据操作语言

数据查询语言


Python与MySQL的交互

安装第三方库:mysql-connector

插入数据步骤

  • 获取连接对象
  • 获取cursor对象
  • 编写SQL语句
  • 执行SQL语句
  • 提交事务

批量插入数据

  • 获取连接对象
  • 获取cursor对象
  • 编写SQL语句
  • 使用列表赋值
  • 调用executemany()执行sql语句
  • 提交事务

查询操作步骤

  • 获取连接对象
  • 获取cursor对象
  • 编写SQL语句
  • 执行SQL语句
  • 调用fetchall()方法获取返回结果,结果为列表类型
  • 遍历列表

更新与删除数据

  • 获取连接对象
  • 获取cursor对象
  • 编写SQL语句
  • 执行SQL语句
  • 提交事务

案例1

import mysql.connector

# 创建连接对象
conn = mysql.connector.connect(host='localhost', user='root', passwd='root', database='test',
                               auth_plugin='mysql_native_password')
# print(conn)
mycursor = conn.cursor()

# 编写sql语句
sql = 'insert into dept (deptno,dname,loc) values (%s,%s,%s)'
val = (50, '开发部', '北京')

# 执行sql语句
mycursor.execute(sql, val)

# 提交
conn.commit()
print(mycursor.rowcount, '记录插入成功')

案例2

import mysql.connector

# 创建连接对象
conn = mysql.connector.connect(host='localhost', user='root', passwd='root', database='test',
                               auth_plugin='mysql_native_password')
# print(conn)
mycursor = conn.cursor()

# 编写sql语句
sql = 'insert into dept (deptno,dname,loc) values (%s,%s,%s)'

vals = [
    (60, '财务部', '上海'),
    (70, '测试部', '长春'),
    (80, '市场部', '深圳')
]
mycursor.executemany(sql, vals)

conn.commit()
print(mycursor.rowcount, '记录插入成功')

案例3

import mysql.connector

# 创建连接对象
conn = mysql.connector.connect(host='localhost', user='root', passwd='root', database='test',
                               auth_plugin='mysql_native_password')
# print(conn)
mycursor = conn.cursor()
# 编写SQL语句
# sql='update dept set dname="Python部门" where deptno=50'
sql = 'delete from dept where deptno=80'
# 执行SQL语句
mycursor.execute(sql)
conn.commit()
print(mycursor.rowcount, '删除成功')

案例4

import mysql.connector

# 创建连接对象
conn = mysql.connector.connect(host='localhost', user='root', passwd='root', database='test',
                               auth_plugin='mysql_native_password')
# print(conn)
mycursor = conn.cursor()
# 编写sql语句
sql = 'select * from dept'
# 执行查询操作
mycursor.execute(sql)
# 获取所有查询的记录
myresult = mycursor.fetchall()
print(type(myresult))
for item in myresult:
    print(item)

案例5

链家二手房成交量

import requests
from bs4 import BeautifulSoup
import mysql.connector


class LianJiaSpider():
    mydb = mysql.connector.connect(host='localhost', user='root', passwd='root', database='test',
                                   auth_plugin='mysql_native_password')
    mycursor = mydb.cursor()

    def __init__(self):
        self.url = 'https://bj.lianjia.com/chengjiao/pg{0}/'
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36'}

    def send_request(self, url):
        resp = requests.get(url, headers=self.headers)
        if resp.status_code == 200:
            return resp

    def parse_html(self, resp):
        lst = []
        html = resp.text
        bs = BeautifulSoup(html, 'lxml')
        ul = bs.find('ul', class_='listContent')
        li_list = ul.find_all('li')
        for item in li_list:
            title = item.find('div', class_='title').text
            houseInfo = item.find('div', class_="houseInfo").text
            positionInfo = item.find('div', class_='positionInfo').text
            dealDate = item.find('div', class_='dealDate').text
            number = item.find('span', class_='number').text + '万'
            unitPrice = item.find('div', class_='unitPrice').text
            dealHouseTxt = item.find('span', class_='dealHouseTxt')
            if dealHouseTxt != None:
                dealHouseTxt = dealHouseTxt.text
            else:
                dealHouseTxt = ''
            span = item.find('span', class_='dealCycleTxt')
            span_list = span.find_all('span')
            agent_name = item.find('a.txt', class_='agent_name').text

            # print(agent_name)
            lst.append((title, houseInfo, positionInfo, dealDate, number, unitPrice, dealHouseTxt, span_list[0].text,
                        span_list[1].text, agent_name))
        # print(lst)
        self.save(lst)  # 调用存储数据的方法

    def save(self, lst):
        sql = 'insert into tb_lianjia (title,houseInfo,positionInfo,dealDate,totalPrice,unitPrice,dealHouseTxt,deal_money,deal_date,agent_name) values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
        self.mycursor.executemany(sql, lst)
        self.mydb.commit()
        print(self.mycursor.rowcount, '插入完毕')

    def start(self):
        for i in range(1, 2):
            full_url = self.url.format(i)
            resp = self.send_request(full_url)
            # print(resp.text)
            self.parse_html(resp)


if __name__ == '__main__':
    lianjia = LianJiaSpider()
    lianjia.start()