本期内容:(1)继续完善评论数据的爬取 (2) 代理的使用
1 创建数据库表
上期我们只打印了爬取到的数据,并没有入库,保存到数据,先执行以下SQL:
CREATE TABLE tb_comment (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '自增主键',
tid VARCHAR(255) COMMENT '景点ID',
username VARCHAR(255) COMMENT '评论的用户名',
avatar VARCHAR(255) COMMENT '评论用户的头像 URL',
comments TEXT COMMENT '评论的内容',
label VARCHAR(20) COMMENT '情感分析分类',
score decimal(10,2) comment '情感分析得分'
) COMMENT '评论数据表';
后续我们要做情感分析,所以预留了情感分析的字段。
2 编写管道
上一期已经把解析的代码写的差不多了,遗留了管道部分未写,下面补全:
# 保存mysql 景点评论
class TourCommentPipeline:
def __init__(self, crawler):
self.crawler = crawler
# 连接 MySQL 数据库
self.db = pymysql.connect(
host=self.crawler.settings.get('DB_HOST'),
user=self.crawler.settings.get('DB_USER'),
password=self.crawler.settings.get('DB_PASS'),
database=self.crawler.settings.get('DB_DATABASE'),
charset=self.crawler.settings.get('DB_CHARSET')
)
self.cursor = self.db.cursor()
@classmethod
def from_crawler(cls, crawler):
return cls(crawler)
def process_item(self, item, spider):
# 检查数据是否已存在
sql = "SELECT COUNT(*) FROM tb_comment WHERE tid = %s and username= %s "
self.cursor.execute(sql, (item["tid"], item['username']))
count = self.cursor.fetchone()[0]
if count > 0:
spider.log(f"Data already exists: {item['tid'], item['username']}")
return item
# 插入新数据
sql = ("INSERT INTO tb_comment (tid, username, avatar, comments) "
"VALUES (%s, %s, %s, %s)")
values = (
item["tid"],
item["username"],
item["avatar"],
item["comments"],
)
try:
self.cursor.execute(sql, values)
self.db.commit()
self.total_items += 1
spider.log(f"Saved data: {item['username']}")
except pymysql.Error as e:
self.db.rollback()
spider.log(f"Error saving data: {item['username']} - {e}")
return item
def close_spider(self, spider):
self.cursor.close()
self.db.close()
3 测试结果
测试爬取的结果是不太给力的,基本上爬取没多少时间,就会报ConnectionRefusedError
的异常,原因应该是目标网站已经监测到我们的爬取行为了。
中间尝试了调低scrapy的并发量和增加每次下载的延时,在settings.py里
# Configure maximum concurrent requests performed by Scrapy (default: 16)
CONCURRENT_REQUESTS = 2 #并发2,甚至我试过1,然并卵
# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 2 # 这个调低了,爬取速度巨慢好吗?
修改这个参数会降低爬取效率,而且并不一定能绕开请求拒绝的问题(再说如果爬取非常慢还有啥意义呢 😭 )
4 代理 😎
为了改变这个局面,在爬虫中需要加入代理机制,本期的话先给出提取代理服务器的代码并且做一个简单的访问测试。
首先需要一个代理的来源,这里找了一家服务商(具体名字不提供了),提取的代码如下:
def get_proxy(key, num):
response = requests.get(f'https://******/get?key={key}&num={num}')
if response.status_code == 200:
proxies = []
ip_pool = []
proxy_data = response.json()
# 假设返回的JSON数据中包含代理IP地址
# 例如: {"proxy": "http://123.45.67.89:8080"}
for p in proxy_data['data']:
# print(p)
proxies.append(p['server']) #要用的是这个地址
ip_pool.append(p['proxy_ip'])
print('ip_pool:', ip_pool)
return proxies
else:
return []
返回的是一个代理服务器的列表,例如: [ 123.45.67.89:8080]
然后怎么测试这个呢? 可以写如下demo代码:
# 测试通过代理访问网站
import requests
def test_proxy_request():
ip = "http://123.45.67.89:8080"
# 代理设置
proxy = {
'http': 'http://代理服务商可能提供的KEY,密码之类的前缀' + ip,
}
# 要访问的目标 URL
url = 'https://place.qyer.com/poi/V2UJYlFiBzRTZVI7Cmk/review/'
try:
# 通过代理发送请求
response = requests.get(url, proxies=proxy, timeout=10)
# 检查返回状态码
if response.status_code == 200:
print("访问成功,返回状态码为200")
else:
print(f"访问失败,返回状态码为{response.status_code}")
except requests.exceptions.RequestException as e:
print(f"请求出错: {e}")
# 运行测试
test_proxy_request()
那在scrapy中怎么使用代理,也是非常简单,只需要修改precess_request方法:
def process_request(self, request, spider):
if self.proxies == []:
self.update_proxies()
proxy = random.choice(self.proxies)
# print(f"目前使用的代理是:http://{proxy}")
request.meta['proxy'] = f"http://{self.user}:{self.passw}@{proxy}" # 这个是我使用的代理的拼写方法,不同的服务商可能不同,这行需要修改!
更新代理池的方法,这里每次更新16个代理地址,可以根据实际情况调整:
def update_proxies(self):
self.proxies = get_proxy(self.user, 16)
5 小结
本期通过设置代理实现了对评论数据的爬取和入库,不过实际使用过程中,代理池是需要更新的,还有很多异常情况需要处理,需要优化的细节很多,请关注本专栏,麦麦会在后续的博文中给大家带来。