数据采集第四次作业
数据采集与融合技术第四次作业
102202122 张诚坤
gitee链接:https://gitee.com/zhang-chengkun666/data-collection-and-fusion/issues/IBBX74
一、作业①
(1)作业要求
熟练掌握 Selenium 查找 HTML 元素、爬取 Ajax 网页数据、等待 HTML 元素等内容。
候选网站:东方财富网:http://quote.eastmoney.com/center/gridlist.html#hs_a_board
1.1核心代码
scr ape_board方法:爬取特定板块数据的方法。首先打开东方财富网的板块列表页面,然后根据传入的板块名称点击相应的链接。页面加载后,它提取表格中的数据,并调用save_to_db方法将数据保存到数据库。
python
def scrape_board(self, board_name, table_name):
# 点击不同的板块
self.driver.get("https://quote.eastmoney.com/center/gridlist.html")
time.sleep(2)
# 根据板块名称点击相应链接
board_links = {
"hs_a": "#hs_a_board",
"sh_a": "#sh_a_board",
"sz_a": "#sz_a_board"
}
board_link = self.driver.find_element(By.CSS_SELECTOR, f'a[href="{board_links[board_name]}"]')
board_link.click()
time.sleep(3) # 等待页面加载
# 提取数据
rows = self.driver.find_elements(By.XPATH, '//tr[@class="odd" or @class="even"]')
for row in rows:
tds = row.find_elements(By.TAG_NAME, "td")
if len(tds) >= 14:
data = [td.text for i, td in enumerate(tds) if i!= 3][:14]
item = {
'id': data[0],
'code': data[1],
'name': data[2],
'price': data[3],
'change_percent': self.clean_percent(data[4]),
'change_amount': data[5],
'volume': self.convert_unit(data[6]),
'amount': self.convert_unit(data[7]),
'amplitude': self.clean_percent(data[8]),
'high': data[9],
'low': data[10],
'open_price': data[11],
'close_price': data[12]
}
self.save_to_db(item, table_name)
scrape_all_boards方法:爬取所有板块数据的方法。它调用scrape_board方法三次,分别爬取沪深 A 股、上证 A 股和深证 A 股的数据。
def scrape_all_boards(self):
# 爬取三个板块的数据
self.scrape_board("hs_a", "quotes_hs_a")
self.scrape_board("sh_a", "quotes_sh_a")
self.scrape_board("sz_a", "quotes_sz_a")
def create_table(self, table_name):
# 创建表的 SQL 语句
create_table_sql = f"""
CREATE TABLE IF NOT EXISTS {table_name} (
# 略
)
"""
self.cursor.execute(create_table_sql)
save_to_db方法:将数据保存到数据库的方法。
def save_to_db(self, item, table_name):
# 插入数据到指定的表中
sql = f"""
INSERT INTO {table_name} (id, code, name, price, change_percent, change_amount, volume, amount, amplitude, high, low, open_price, close_price)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
self.cursor.execute(sql, (
item['id'], item['code'], item['name'], item['price'],
item['change_percent'], item['change_amount'], item['volume'],
item['amount'], item['amplitude'], item['high'],
item['low'], item['open_price'], item['close_price']
))
self.connection.commit()
1.1.2 实验结果
将数据爬取并保存到 MySQL 中三个不同的表格。
1.2 实验问题及解决方法
问题:上述的 selenium 爬取三个模块的方法使用的是分别调用三次爬取的方法,每爬取一个模块的信息后退出重新爬取,是否可以在一次性多次爬取。
解决方法?
方法:
1.循环调用爬取函数:在单个函数中循环调用爬取模块的方法,而不是分开调用,这样可以减少重复代码和启动浏览器的时间。
2.使用多线程或异步处理:利用 Python 的多线程或异步 IO 来同时爬取多个模块,提高效率。
3.优化等待策略:使用更智能的等待策略,如 WebDriverWait,等待所有模块的特定元素加载完成后再进行爬取,而不是等待整个页面加载完成。
1.2.2 实验心得
熟练掌握 Selenium:熟悉 Selenium 的各种方法和属性对于高效定位元素和爬取数据至关重要。
二、作业②
(1)作业要求
熟练掌握 Selenium 查找 HTML 元素、实现用户模拟登录、爬取 Ajax 网页数据、等待 HTML 元素等内容。
候选网站:中国 mooc 网:https://www.icourse163.org
2.1 核心代码
用户模拟登录界面:
driver.get("https://www.icourse163.org")
time.sleep(2)
# 点击“登录/注册”按钮
login_register_button = WebDriverWait(driver, 2).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "div._3uWA6[role='button']"))
)
login_register_button.click()
# 切换到 iframe 并输入手机号和密码
iframe = WebDriverWait(driver, 2).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "iframe[src*='index_dl2_new.html']"))
)
driver.switch_to.frame(iframe)
phone_input = WebDriverWait(driver, 2).until(
EC.presence_of_element_located((By.ID, "phoneipt"))
)
phone_input.send_keys("19577660828") # 替换为实际手机号
password_input = WebDriverWait(driver, 2).until(
EC.presence_of_element_located((By.CLASS_NAME, "j-inputtext"))
)
password_input.send_keys("1314520.abc") # 替换为实际密码
login_button = WebDriverWait(driver, 5).until(
EC.element_to_be_clickable((By.ID, "submitBtn"))
)
login_button.click()
driver.switch_to.window(driver.window_handles[-1]) # 切换到新窗口
XPath 定位和点击元素:
agree_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, '//button[ @class="btn ok"]'))
)
agree_button.click()
# 定位到“国家精品课”链接并点击
national_course_link = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, '//a[@href="https://www.icourse163.org/channel/2001.htm"]'))
)
national_course_link.click()
循环点击课程、数据爬取:
for i, course_item in enumerate(course_items):
course_item.click()
time.sleep(5)
new_window_handle = driver.window_handles[-1]
driver.switch_to.window(new_window_handle)
cCourse = driver.find_element(By.XPATH, '//span[@class="course-title f-ib f-vam"]').text
cCollege = driver.find_element(By.XPATH, "//img[@class='u-img']").get_attribute("alt")
cTeacher = driver.find_element(By.XPATH, '//div[@class="cnt f-fl"]//h3[@class="f-fc3"]').text
cCount = driver.find_element(By.XPATH, '//span[@class="count"]').text
cProcess = driver.find_element(By.XPATH, "//div[@class='course-enroll-info_course-info_term-info_term-time']").text
cBrief = driver.find_element(By.XPATH, "//div[@class='course-heading-intro_intro']").text
with open(csv_file, mode="a", encoding="utf-8", newline='') as file:
writer = csv.writer(file)
writer.writerow([serial_number, cCourse, cCollege, cTeacher, cCount, cProcess, cBrief])
driver.close()
driver.switch_to.window(driver.window_handles[-1])
time.sleep(2)
if (1 + i) % 5 == 0:
special_div = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, '_2uD4W') and contains(@class, '_2zoND')]"))
)
special_div.click()
time.sleep(2)
写入数据库:
db_connection = mysql.connector.connect(
host="localhost", # 数据库地址
user="root", # 数据库用户名
password="789789789", # 数据库密码
database="666_zckzckzck" # 数据库名
)
# 创建数据库表格
cursor = db_connection.cursor()
# 如果表已经存在,先删除它
cursor.execute("DROP TABLE IF EXISTS course_data")
# 创建表格
create_table_query = """
CREATE TABLE course_data (
cursor.execute(create_table_query)
# 提取“参与人数”列中的数字
course_data['participants'] = course_data['参与人数'].apply(lambda x: int(re.sub(r'\D', '', str(x))))
# 去掉“开课时间:”部分
course_data['course_time'] = course_data['时间'].apply(lambda x: re.sub(r'开课时间:', '', str(x)))
# 插入数据到 MySQL 表格中
for index, row in course_data.iterrows():
insert_query = """
INSERT INTO course_data (id, course_name, school, teacher, participants, course_time, course_details)
VALUES (%s, %s, %s, %s, %s, %s, %s)
"""
cursor.execute(insert_query,
))
2.2 实验结果
成功将爬取的数据写入数据库。
2.3 实验问题及解决方法
问题 2:点击进入课程后无法爬取数据。
解决方法:
因为打开新窗口或新标签页后未正确切换浏览器窗口导致的。Selenium 在打开新的页面时需要切换到新的窗口 / 标签页来进行操作。
使用 driver.switch_to.window () 方法切换到新窗口,确保在正确的页面上提取数据。
2.2.2 实验心得
在使用 Selenium 对网页进行自动化操作的过程中,其与动态页面的互动性以及稳定性是需要重点关注的方面。
动态页面的互动性与稳定性
对于那些采用异步加载内容机制的动态网页而言,Selenium 的操作效果与页面加载状态以及元素的可见性紧密相关。由于动态网页的内容并非一次性全部呈现,而是随着页面的运行逐步加载,这就要求我们在编写自动化脚本时,必须更加谨慎地去处理元素的操作时机。此时,WebDriverWait 和 expected_conditions 这两个工具的作用就凸显出来了。借助它们,我们能够精准地判断每个元素是否已经完成加载,并且确认其处于可操作的状态,进而避免因元素尚未加载完毕或者不可见而导致的操作失败情况,保障整个自动化流程的稳定性和准确性。
处理 iframe 问题
在面对网页中存在 iframe(嵌入式框架)的情况时,Selenium 有着特定的操作要求。如果需要对 iframe 内部的元素进行访问和操作,比如在执行登录操作,要定位用户名、密码等输入框时,首先必须要切换到相应的 iframe 框架才行。倘若忽略了这一关键步骤,直接去查找框架内的元素,是无法获取到的,会导致后续操作无法正常开展。所以,在遇到涉及 iframe 的问题时,操作人员务必要格外留意页面的切换逻辑以及元素在不同框架下的定位方式,确保每一步操作都准确无误地针对目标 iframe 框架内的元素来进行。
新窗口或标签页的切换
在网页交互中,常常会出现这样的情况:当用户点击某些特定链接后,页面会随之打开一个新的标签页或者窗口。在利用 Selenium 进行数据抓取等自动化操作时,面对这种新开窗口或标签页的场景,就需要及时切换到新打开的窗口,这样才能继续顺利地抓取其中的数据。而实现这一切换的关键操作,便是通过 driver.window_handles 来获取各个窗口的句柄,然后依据实际需求准确地进行窗口切换,这一步骤在整个自动化流程中是不可或缺的,直接关系到能否完整、准确地抓取到全部所需数据。
三、作业③