数据采集作业3
第三次作业
一、作业内容
作业①:
要求:指定一个网站,爬取这个网站中的所有的所有图片,例如:中国气象网(http://www.weather.com.cn)。实现单线程和多线程的方式爬取。
–务必控制总页数(学号尾数2位)、总下载的图片数量(尾数后3位)等限制爬取的措施。
输出信息: 将下载的Url信息在控制台输出,并将下载的图片存储在images子文件中,并给出截图。
查看作业3代码:作业3代码文件夹
单线程核心代码:3.1.1.py
限制最多爬取3页,最多下载103张图片
class SingleThreadImageCrawler:
def __init__(self, max_pages=3, max_images=103):
self.base_url = "http://www.weather.com.cn"
self.max_pages = max_pages
self.max_images = max_images
self.downloaded_images = 0
self.visited_pages = set()
self.downloaded_urls = set() # 用于记录已经下载的图片URL,防止重复
使用BeautifulSoup解析HTML,找到所有标签
soup = BeautifulSoup(response.text, 'html.parser')
self.visited_pages.add(url)
# 查找所有图片标签
img_tags = soup.find_all('img')
img_urls = []
单线程运行截图:



多线程核心代码:3.1.2.py
页面解析和图片下载分离,可以并行执行
self.page_queue = queue.Queue() # 页面URL队列
self.image_queue = queue.Queue() # 图片URL队列
使用锁保护共享资源
self.lock = threading.Lock() # 线程锁
线程管理和协调
# 启动页面爬取线程
page_threads = []
for i in range(min(self.num_threads, 2)): # 使用较少的线程进行页面爬取
thread = threading.Thread(target=self.page_crawler)
thread.daemon = True # 守护线程,主线程结束时自动退出
thread.start()
page_threads.append(thread)
# 等待页面队列处理完成
self.page_queue.join()
# 启动图片下载线程
download_threads = []
for i in range(self.num_threads):
thread = threading.Thread(target=self.image_downloader)
thread.daemon = True
thread.start()
download_threads.append(thread)
# 等待图片队列处理完成
self.image_queue.join()
多线程运行截图:



心得体会
多线程通过并行处理,显著减少总执行时间,比单线程要快
作业②
要求:熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;使用scrapy框架+Xpath+MySQL数据库存储技术路线爬取股票相关信息。
候选网站:东方财富网:https://www.eastmoney.com/
输出信息:MySQL数据库存储
查看作业3代码:作业3代码文件夹
核心代码:stock_spider
items.py定义爬虫要提取的数据字段,后续可以通过Pipeline进行各种处理操作
class StockSpiderItem(scrapy.Item):
# 定义数据字段
stock_code = scrapy.Field() # 股票代码
stock_name = scrapy.Field() # 股票名称
latest_price = scrapy.Field() # 最新报价
change_percent = scrapy.Field() # 涨跌幅
change_amount = scrapy.Field() # 涨跌额
volume = scrapy.Field() # 成交量(手)
turnover = scrapy.Field() # 成交额
amplitude = scrapy.Field() # 振幅(%)
high = scrapy.Field() # 最高
low = scrapy.Field() # 最低
open_price = scrapy.Field() # 今开
close_price = scrapy.Field() # 昨收
crawl_time = scrapy.Field() # 爬取时间
Pipeline用于数据清理,存储.这里将Item数据序列化到数据库
def process_item(self, item, spider):
if not hasattr(self, 'connection') or not self.connection:
spider.logger.error("数据库连接不可用")
return item
try:
insert_sql = """
INSERT INTO stock_data
(stock_code, stock_name, latest_price, change_percent, change_amount,
volume, turnover, amplitude, high, low, open_price, close_price, crawl_time)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE
latest_price=VALUES(latest_price),
change_percent=VALUES(change_percent),
change_amount=VALUES(change_amount),
volume=VALUES(volume),
turnover=VALUES(turnover),
amplitude=VALUES(amplitude),
high=VALUES(high),
low=VALUES(low),
open_price=VALUES(open_price),
close_price=VALUES(close_price)
"""
adapter = ItemAdapter(item)
values = (
adapter.get('stock_code', ''),
adapter.get('stock_name', ''),
adapter.get('latest_price', 0),
adapter.get('change_percent', 0),
adapter.get('change_amount', 0),
adapter.get('volume', ''),
adapter.get('turnover', ''),
adapter.get('amplitude', ''),
adapter.get('high', 0),
adapter.get('low', 0),
adapter.get('open_price', 0),
adapter.get('close_price', 0),
adapter.get('crawl_time', '')
)
self.cursor.execute(insert_sql, values)
self.connection.commit()
spider.logger.info(f"成功存储股票数据: {adapter.get('stock_code')} - {adapter.get('stock_name')}")
except Exception as e:
spider.logger.error(f"插入数据失败: {str(e)}")
if hasattr(self, 'connection'):
self.connection.rollback()
return item
运行截图:



查看总记录数

查看前10条记录

心得体会:
学会了使用mysql存储数据,以及使用spider框架进行爬虫。并且对Scrapy的结构更加清晰。
作业③:
要求:熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;使用scrapy框架+Xpath+MySQL数据库存储技术路线爬取外汇网站数据。
候选网站:中国银行网:https://www.boc.cn/sourcedb/whpj/
输出信息:
查看作业3代码:作业3代码文件夹
核心代码:forex_crawler
1、根据页面结构分析


2、跳过表头行,只获取数据行
rows = response.xpath('//div[@class="publish"]//tr[position()>1]')
3、逐字段提取
for row in rows:
# 检查是否是数据行(有8个td)
tds = row.xpath('./td')
if len(tds) == 8:
item = ForexItem()
# 使用XPath提取每个字段
item['currency_name'] = self.extract_text(tds[0]) # 第1个td:货币名称
item['tbp'] = self.extract_text(tds[1]) # 第2个td:现汇买入价
item['cbp'] = self.extract_text(tds[2]) # 第3个td:现钞买入价
item['tsp'] = self.extract_text(tds[3]) # 第4个td:现汇卖出价
item['csp'] = self.extract_text(tds[4]) # 第5个td:现钞卖出价
item['middle_rate'] = self.extract_text(tds[5]) # 第6个td:中行折算价
item['publish_date'] = self.extract_text(tds[6]) # 第7个td:发布日期
item['publish_time'] = self.extract_text(tds[7]) # 第8个td:发布时间
4、由于输出结果只需要6个字段,所有在 pipelines.py 的数据库插入语句中,只选择了需要的6个字段
insert_sql = """
INSERT INTO forex_data
(currency_name, tbp, cbp, tsp, csp, publish_time) -- 这里只有6个字段
VALUES (%s, %s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE
tbp=VALUES(tbp),
cbp=VALUES(cbp),
tsp=VALUES(tsp),
csp=VALUES(csp),
publish_time=VALUES(publish_time)
"""
运行结果:


心得体会:
学会使用XPath定位到包含数据的表格行,并逐个提取每个单元格的数据。并且通过Pipeline在爬取过程中实时存储数据

浙公网安备 33010602011771号