东方财富网股吧爬虫案例
运行环境:
pip install -i https://mirrors.aliyun.com/pypi/simple colorama
pip install -i https://mirrors.aliyun.com/pypi/simple requests
pip install -i https://mirrors.aliyun.com/pypi/simple parsel
pip install -i https://mirrors.aliyun.com/pypi/simple pymysql
MySQL 数据库表结构
create table comment
(
id varchar(200) not null primary key,
read_count int null,
reply int null,
title varchar(200) null,
title_url varchar(200) null,
author varchar(200) null,
author_url varchar(200) null,
update_time varchar(200) null
);
create database spiders default character set utf8mb4;
代码分析:
1.构建请求头信息
host:换成你自己的数据地址,一般本地库都是 localhost
user:替换成自己的用户名
database:需要自建一个 spiders 数据库
port:端口号,MySQL 默认 3306
请求头字典里有一个 Cookie 和 Host 在测试的过程中,如果只加一个 User-Agent 返回的结果是 403 状态码,没有特殊反扒,一般情况下补充请求头信息即可。
base_url = 'https://guba.eastmoney.com'
db = pymysql.connect(host='localhost', user='root', password='ambition', database='spiders', port=3306)
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.'
'0.0 Safari/537.36',
'Host': 'guba.eastmoney.com',
'Cookie': 'Cookie 信息'
}
2.获取内容函数
其中 count 参数是为了分页做准备,在第一页的时候 URL 没有拼接_1 所有做了个判断。
def crawl_content(count=1):
"""
根据 URL 获取内容
:param count: 页码
:return: 获取的 HTML 内容
"""
url = base_url + '/list,waihui.html'
if count != 1:
# count != 1 的时候就是指定页数的评价
url = base_url + '/list,waihui' + f'_{count}' + '.html'
try:
print(Fore.BLACK + '-' * 50, '正在获取:' + url)
response = requests.get(url, headers=headers)
# 个人经历的判断
if response.status_code == http.HTTPStatus.PERMANENT_REDIRECT: # 请求头信息不全可能会触发 redirect 重定向错误
print('请检查请求头(headers)信息!!')
# 最后请求的内容也是空直接结束程序
sys.exit()
else:
if response.text.find('<ul class="tab_content"') > 0:
return response.text
else:
print('-' * 50, '获取的:(' + url + ')内容可能不正确!!')
except urllib.error.URLError as e:
print('URLError: ', '\n', url, '\n', e.reason)
sys.exit()
3.解析内容
css 和 xpath 选择节点根据个人经验来,本人经验不足可能有更简便的写法。
def spider_out_comment(content):
"""
根据获取的内容解析需要的内容
:return:
"""
# with open('1.html', 'r', encoding='utf-8') as file:
# content = file.read()
# 创建 selector 对象读取解析内容
selector = Selector(text=content)
list_body = selector.xpath('//li[contains(@class, "defaultlist")]/table[contains(@class, "default_list")]/tbody[c'
'ontains(@class, "listbody")]').xpath('.//tr[contains(@class, "listitem")]')
data = [] # 存储信息
for item in list_body:
read_count = item.css('div.read::text').get() # 阅读量
reply = item.css('div.reply::text').get() # 评论数
title = item.css('div.title > a::text').get() # 标题
title_url = item.css('div.title > a').attrib['href'] # 标题对应的链接
author = item.css('div.author > a::text').get() # 作者
author_url = item.css('div.author > a').attrib['href'] # 作者主页
# 获取更新时间
update_time = item.xpath('.//div[contains(@class, "update")]/text()').get()
data.append({
'id': re.findall('[0-9]+', title_url)[0],
'read_count': read_count,
'reply': reply,
'title': title,
'title_url': title_url,
'author': author,
'author_url': author_url,
'update_time': update_time
})
save_mysql(data)
4.保存解析的内容到数据库
def save_mysql(data):
"""
保存信息
:return:
"""
cursor = db.cursor()
print(Fore.BLACK + '-' * 50 + '保存获取的信息')
count = 0
for i in range(len(data)):
keys = ', '.join(data[i].keys()) # 使用变量名作为字段名
values = ', '.join(['%s'] * len(data[i]))
sql = f'insert into comment ({keys}) values ({values})'
try:
cursor.execute(sql, tuple(data[i].values()))
except pymysql.Error:
print(Fore.RED, '已经存在了:', data[i]['id'])
db.rollback()
else:
count += 1
db.commit()
print(Fore.GREEN + '-' * 50 + '保存成功!共有 ' + str(count) + ' 条')
关注公众号赠送 python 书籍
关注公众号私信001,发送对应书籍。
完整代码
注意更换数据库地址,以及 cookie 请求头信息。
import http
import re
import sys
import urllib.error
import requests
import pymysql
from parsel import Selector
from colorama import Fore, init
init() # 用来调整 print 打印颜色
base_url = 'https://guba.eastmoney.com'
db = pymysql.connect(host='localhost', user='root', password='ambition', database='spiders', port=3306)
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.'
'0.0 Safari/537.36',
'Host': 'guba.eastmoney.com',
'Cookie': 'HAList=ty-133-USDCNH-%u7F8E%u5143%u79BB%u5CB8%u4EBA%u6C11%u5E01; qgqp_b_id=41cbf4607be7c6b3e4c71fa5bd588'
'132; websitepoptg_api_time=1710501153250; st_si=57729005964046; st_asi=delete; st_pvi=40258994733769; st'
'_sp=2024-03-15%2019%3A12%3A33; st_inirUrl=https%3A%2F%2Fcn.bing.com%2F; st_sn=6; st_psi=2024031519150576'
'9-117001356555-7675512820'
}
def crawl_content(count=1):
"""
根据 URL 获取内容
:param count: 页码
:return: 获取的 HTML 内容
"""
url = base_url + '/list,waihui.html'
if count != 1:
# count != 1 的时候就是指定页数的评价
url = base_url + '/list,waihui' + f'_{count}' + '.html'
try:
print(Fore.BLACK + '-' * 50, '正在获取:' + url)
response = requests.get(url, headers=headers)
# 个人经历的判断
if response.status_code == http.HTTPStatus.PERMANENT_REDIRECT: # 请求头信息不全可能会触发 redirect 重定向错误
print('请检查请求头(headers)信息!!')
# 最后请求的内容也是空直接结束程序
sys.exit()
else:
if response.text.find('<ul class="tab_content"') > 0:
return response.text
else:
print('-' * 50, '获取的:(' + url + ')内容可能不正确!!')
except urllib.error.URLError as e:
print('URLError: ', '\n', url, '\n', e.reason)
sys.exit()
def spider_out_comment(content):
"""
根据获取的内容解析需要的内容
:return:
"""
# with open('1.html', 'r', encoding='utf-8') as file:
# content = file.read()
# 创建 selector 对象读取解析内容
selector = Selector(text=content)
list_body = selector.xpath('//li[contains(@class, "defaultlist")]/table[contains(@class, "default_list")]/tbody[c'
'ontains(@class, "listbody")]').xpath('.//tr[contains(@class, "listitem")]')
data = [] # 存储信息
for item in list_body:
read_count = item.css('div.read::text').get() # 阅读量
reply = item.css('div.reply::text').get() # 评论数
title = item.css('div.title > a::text').get() # 标题
title_url = item.css('div.title > a').attrib['href'] # 标题对应的链接
author = item.css('div.author > a::text').get() # 作者
author_url = item.css('div.author > a').attrib['href'] # 作者主页
# 获取更新时间
update_time = item.xpath('.//div[contains(@class, "update")]/text()').get()
data.append({
'id': re.findall('[0-9]+', title_url)[0],
'read_count': read_count,
'reply': reply,
'title': title,
'title_url': title_url,
'author': author,
'author_url': author_url,
'update_time': update_time
})
save_mysql(data)
def save_mysql(data):
"""
保存信息
:return:
"""
cursor = db.cursor()
print(Fore.BLACK + '-' * 50 + '保存获取的信息')
count = 0
for i in range(len(data)):
keys = ', '.join(data[i].keys()) # 使用变量名作为字段名
values = ', '.join(['%s'] * len(data[i]))
# on duplicate key update 当 key 一直的时候不做新增操作,修改
sql = f'insert into comment ({keys}) values ({values})'
try:
cursor.execute(sql, tuple(data[i].values()))
except pymysql.Error:
print(Fore.RED, '已经存在了:', data[i]['id'])
db.rollback()
else:
count += 1
db.commit()
print(Fore.GREEN + '-' * 50 + '保存成功!共有 ' + str(count) + ' 条')
if __name__ == '__main__':
for i in range(5):
spider_out_comment(crawl_content(i + 1))
db.close()