【Python爬虫】专栏简介:本专栏是 Python 爬虫领域的集大成之作,共 100 章节。从 Python 基础语法、爬虫入门知识讲起,深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑,覆盖网页、图片、音频等各类数据爬取,还涉及数据处理与分析。无论是新手小白还是进阶开发者,都能从中汲取知识,助力掌握爬虫核心技能,开拓技术视野。
在当今数字化时代,电商网站积累了海量的商品信息,这些信息涵盖了产品的价格、销量、用户评价等多维度数据,对于市场分析、商业决策以及消费者洞察具有不可估量的价值。通过对电商网站数据的深入挖掘,企业能够精准把握市场趋势,了解消费者需求,优化产品策略,从而在激烈的市场竞争中占据优势。
然而,大多数电商网站如淘宝、京东等采用了动态网页技术,商品信息并非一次性全部加载在初始页面中,而是通过 JavaScript 动态加载。这一技术虽然提升了用户体验,却给数据爬取带来了极大的挑战。同时,电商网站为了保护数据安全和自身运营秩序,设置了严格的登录验证与反爬虫机制,进一步增加了爬取的难度。
本章节将深入探讨如何使用 Python 实现对动态加载商品信息的电商网站的爬取,详细阐述应对登录验证与反爬虫机制的策略,以及爬取后的数据清洗与整理方法,帮助读者掌握从复杂电商网站获取高质量数据的核心技能。
在开始爬取电商网站之前,需要确保 Python 环境已经搭建好,并且安装了必要的库。以下是主要库的安装与配置说明:
以淘宝为例,其页面结构复杂,商品信息通过 JavaScript 动态加载。在浏览器中打开淘宝商品搜索页面,通过开发者工具(如 Chrome 浏览器的 F12)可以分析其页面结构和动态加载方式。
以淘宝为例,其登录方式较为多样,常见的有手机号登录、淘宝账号登录以及第三方平台(如支付宝)登录 。当选择手机号或淘宝账号登录时,需要准确输入对应的账号和密码。为了防止恶意登录和自动化攻击,淘宝采用了多种验证码类型,其中滑块验证码是较为常见的一种。
滑块验证码的原理是基于图像识别技术,用户需要将滑块拖动到指定位置,以验证其是人类操作。通常,滑块验证码会根据图片中的特定区域,如颜色、形状、纹理等进行识别。这种验证码对于爬虫程序来说具有一定难度,因为爬虫程序难以模拟人类的视觉识别能力和精准的操作。
使用 Selenium 库可以有效地模拟浏览器操作,实现登录功能。下面是使用 Selenium 登录淘宝的示例代码:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# 启动浏览器
driver = webdriver.Chrome()
# 打开淘宝登录页面
driver.get("https://login.taobao.com/member/login.jhtml")
# 等待页面加载,切换到账号密码登录
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//a[text()="密码登录"]'))).click()
# 输入账号和密码
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "fm-login-id"))).send_keys("your_username")
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "fm-login-password"))).send_keys("your_password")
# 处理滑块验证码
try:
# 等待滑块验证码出现
slider = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, "geetest_slider_button")))
# 模拟拖动滑块,这里使用第三方打码平台(以超级鹰为例)的思路,先获取验证码图片,发送到打码平台识别
# 实际使用需要安装相关库并注册打码平台账号获取正确的API信息
# 假设获取到的移动距离为distance
distance = 200 # 这里是示例值,实际需要从打码平台获取
# 模拟拖动滑块的操作,这里简单模拟为匀速拖动,实际可以根据人类行为优化
start_x = slider.location['x']
for i in range(0, int(distance), 5):
driver.execute_script(f"arguments[0].style.transform = 'translate3d({start_x + i}px, 0, 0)';", slider)
time.sleep(0.1)
# 点击登录按钮
WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".fm-button.fm-submit.password-login"))).click()
except Exception as e:
print(f"验证码处理或登录过程出现问题: {e}")
# 检查是否登录成功
if "https://www.taobao.com/" in driver.current_url:
print("登录成功")
else:
print("登录失败")
# 关闭浏览器
driver.quit()
在上述代码中,首先通过webdriver.Chrome()启动 Chrome 浏览器并打开淘宝登录页面。然后使用WebDriverWait等待页面元素加载完成,找到账号、密码输入框并输入相应信息。
对于滑块验证码,先等待滑块元素出现,然后可以采用第三方打码平台(如超级鹰)来获取滑块需要移动的距离。这里简单模拟了拖动滑块的操作,实际应用中可以根据人类行为习惯,如先加速后减速的方式来优化拖动轨迹,以更好地绕过验证码检测。最后点击登录按钮,并检查当前页面 URL 来判断是否登录成功。登录成功后,就可以在后续的代码中进行商品信息的爬取操作了。
淘宝作为国内领先的电商平台,拥有一套完善且强大的反爬虫机制,旨在保护平台数据安全、维护用户体验以及保障商家利益。其常见的反爬虫手段涵盖多个维度 :
针对淘宝的反爬虫机制,我们可以采取一系列有效的应对策略,并通过代码实现来绕过这些限制:
import time
import requests
for i in range(10):
response = requests.get('https://example.taobao.com')
print(f"第{i + 1}次请求成功")
time.sleep(4) # 设置请求间隔为4秒
import requests
import random
proxy_list = [
"http://proxy1.example.com:8080",
"http://proxy2.example.com:8080",
"http://proxy3.example.com:8080"
]
proxy = random.choice(proxy_list)
response = requests.get('https://example.taobao.com', proxies={"http": proxy, "https": proxy})
实际应用中,代理 IP 池可以通过购买专业的代理服务或者自行编写爬虫从免费代理 IP 网站获取,但需要注意免费代理 IP 的稳定性和可靠性较低 。同时,要对代理 IP 进行有效性检测,确保其能够正常使用。可以编写一个函数来检测代理 IP 是否可用,示例代码如下:
def check_proxy(proxy):
try:
response = requests.get('https://www.baidu.com', proxies={"http": proxy, "https": proxy}, timeout=5)
if response.status_code == 200:
return True
else:
return False
except Exception:
return False
import requests
import random
user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0"
]
headers = {
"User - Agent": random.choice(user_agents),
"Referer": "https://www.taobao.com"
}
response = requests.get('https://example.taobao.com', headers=headers)
此外,还可以根据不同的请求类型和页面,动态调整请求头中的其他字段,如添加Accept - Encoding、Accept - Language等字段,进一步增强伪装效果 。通过综合运用这些应对策略和代码实现,可以在一定程度上有效地绕过淘宝的反爬虫机制,提高爬虫程序的稳定性和成功率,但同时也要注意遵守法律法规和网站的使用条款,避免对网站造成不必要的负担和影响。
以淘宝为例,当用户在淘宝搜索商品并打开搜索结果页面时,页面初始加载的只是一些基本的 HTML 结构和少量静态数据,如页面布局、导航栏等信息。而商品列表中的商品信息,如商品名称、价格、销量、图片等,是通过 Ajax 技术动态加载的。
当页面加载完成后,浏览器会根据用户的操作(如滚动页面、点击加载更多按钮等),向服务器发送异步请求。这些请求通常是通过 JavaScript 代码中的XMLHttpRequest对象或者fetch API 来实现的。在请求中,会携带一些参数,如搜索关键词、页码、商品类别筛选条件等,服务器根据这些参数,从数据库中查询相应的商品数据,并将数据以 JSON 格式返回给浏览器。浏览器接收到返回的数据后,再通过 JavaScript 代码将这些数据解析并插入到 HTML 页面的相应位置,从而实现商品信息的动态加载。这样,用户在浏览商品列表时,无需重新加载整个页面,就能获取到更多的商品信息,大大提升了用户体验。
利用 Selenium 库可以有效地模拟用户在浏览器中的操作,从而获取动态加载的商品信息。下面以爬取淘宝手机商品信息为例,展示具体的代码实现:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# 启动浏览器
driver = webdriver.Chrome()
# 打开淘宝搜索页面
driver.get("https://s.taobao.com/search?q=手机")
# 等待页面加载,模拟滚动页面以加载更多商品
for i in range(3):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2)
# 等待商品列表加载完成
WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.m-itemlist.items.item')))
# 提取商品信息
products = []
items = driver.find_elements(By.CSS_SELECTOR, '.m-itemlist.items.item')
for item in items:
product = {}
# 商品名称
product['name'] = item.find_element(By.CSS_SELECTOR, '.title').text.strip()
# 商品价格
product['price'] = item.find_element(By.CSS_SELECTOR, '.price').text.strip()
# 商品销量
sales_text = item.find_element(By.CSS_SELECTOR, '.deal-cnt').text.strip()
product['sales'] = sales_text.replace('人付款', '') if '人付款' in sales_text else sales_text
products.append(product)
print(products)
# 关闭浏览器
driver.quit()
在上述代码中,首先使用webdriver.Chrome()启动 Chrome 浏览器,并打开淘宝搜索 “手机” 的页面。然后通过循环调用driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”)模拟滚动页面,每次滚动后等待 2 秒,以便让页面有足够的时间加载更多商品。接着使用WebDriverWait等待商品列表中的所有商品项加载完成。最后,通过 CSS 选择器定位每个商品项,并提取商品名称、价格和销量信息,将其存储在一个列表中并打印出来。
在实际应用中,还可以根据需要进一步扩展代码,如处理翻页操作,以获取更多页面的商品信息 。处理翻页时,可以先找到页面中的下一页按钮元素,然后使用click方法模拟点击操作,每次点击后重复上述获取商品信息的步骤,直到获取完所有需要的页面数据。例如:
# 翻页操作
while True:
try:
# 找到下一页按钮
next_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '.next'))
)
next_button.click()
time.sleep(3)
# 等待新页面商品列表加载完成
WebDriverWait(driver, 10).until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.m-itemlist.items.item'))
)
# 提取新页面商品信息
items = driver.find_elements(By.CSS_SELECTOR, '.m-itemlist.items.item')
for item in items:
product = {}
product['name'] = item.find_element(By.CSS_SELECTOR, '.title').text.strip()
product['price'] = item.find_element(By.CSS_SELECTOR, '.price').text.strip()
sales_text = item.find_element(By.CSS_SELECTOR, '.deal-cnt').text.strip()
product['sales'] = sales_text.replace('人付款', '') if '人付款' in sales_text else sales_text
products.append(product)
except Exception:
break
这样就可以实现对多页商品信息的爬取,从而获取更全面的电商网站商品数据。
在成功爬取电商网站的商品信息后,得到的数据往往存在各种问题,如重复数据、格式不规范、缺失值等,这些问题会影响后续的数据分析和应用。因此,需要对数据进行清洗与整理,以提高数据的质量和可用性。
在爬取电商网站商品信息时,由于各种原因(如多次访问同一页面、网站数据更新机制等),可能会获取到重复的商品数据。这些重复数据不仅会占用额外的存储空间,还会影响数据分析的准确性和效率,因此需要进行去重处理。
一种简单有效的去重方法是使用集合(Set)数据结构。集合是 Python 中的一种无序且元素唯一的数据结构,它可以自动去除重复的元素。在爬取商品信息时,可以将每个商品的唯一标识(如商品 ID)或包含关键信息的元组添加到集合中。例如:
product_ids = set()
unique_products = []
for product in products:
product_id = product['id'] # 假设商品ID为'id'字段
if product_id not in product_ids:
product_ids.add(product_id)
unique_products.append(product)
在上述代码中,首先创建一个空集合product_ids用于存储已出现的商品 ID,然后遍历爬取到的商品列表products。对于每个商品,获取其 ID 并检查是否已在集合中。如果不在集合中,则将该商品 ID 添加到集合中,并将商品添加到unique_products列表中,从而实现去重。
另一种方法是使用哈希算法。哈希算法可以将任意长度的输入数据转换为固定长度的哈希值,且相同的输入数据会得到相同的哈希值。在数据去重中,可以对每个商品的关键信息(如商品 ID、名称、价格等)进行哈希计算,然后将哈希值与已有的哈希值进行比较。如果哈希值相同,则认为是重复数据 。下面是使用哈希算法去重的示例代码:
import hashlib
hash_set = set()
unique_products = []
for product in products:
product_info = f"{product['id']}{product['name']}{product['price']}" # 假设关键信息为ID、名称和价格
hash_value = hashlib.md5(product_info.encode()).hexdigest()
if hash_value not in hash_set:
hash_set.add(hash_value)
unique_products.append(product)
在这段代码中,首先创建一个空集合hash_set用于存储哈希值。然后遍历商品列表,将每个商品的关键信息拼接成一个字符串product_info,对其进行 MD5 哈希计算得到哈希值hash_value。如果该哈希值不在集合中,则将其添加到集合中,并将商品添加到unique_products列表中,以此实现数据去重 。哈希算法的优点是速度快,适用于处理大量数据,但可能会存在哈希冲突(即不同的数据得到相同的哈希值)的情况,需要根据具体情况进行处理。
爬取到的电商商品数据中,价格、日期等字段的格式可能多种多样,不便于后续的数据分析和处理,因此需要对其进行格式规范化。
以价格字段为例,可能存在以下几种格式:“199.00 元”、“$29.99”、“¥99” 等。可以使用正则表达式提取其中的数字部分,并统一转换为浮点数类型。示例代码如下:
import re
for product in products:
price_str = product['price']
match = re.search(r'\d+(\.\d+)?', price_str)
if match:
price = float(match.group())
product['price'] = price
在上述代码中,使用re.search函数查找价格字符串中的数字部分,\d+(.\d+)?这个正则表达式表示匹配一个或多个数字,后面可以跟着一个可选的小数点和一个或多个数字。如果找到匹配的数字,则将其转换为浮点数并更新商品的价格字段。
对于日期字段,可能存在 “2024/01/01”、“2024-01-01”、“01/01/2024” 等不同格式。可以使用datetime模块将其统一转换为标准的日期格式,如 “YYYY - MM - DD”。示例代码如下:
from datetime import datetime
for product in products:
date_str = product['date']
try:
date = datetime.strptime(date_str, '%Y/%m/%d')
product['date'] = date.strftime('%Y-%m-%d')
except ValueError:
try:
date = datetime.strptime(date_str, '%Y-%m-%d')
product['date'] = date.strftime('%Y-%m-%d')
except ValueError:
try:
date = datetime.strptime(date_str, '%m/%d/%Y')
product['date'] = date.strftime('%Y-%m-%d')
except ValueError:
pass
在这段代码中,使用datetime.strptime函数尝试将不同格式的日期字符串解析为datetime对象,然后使用strftime函数将其格式化为 “YYYY - MM - DD” 的标准格式。如果解析失败,则尝试其他可能的日期格式 。通过这样的处理,可以使日期字段的格式统一,便于后续的日期计算和分析。
在爬取电商商品数据时,由于网络问题、网页结构变化等原因,可能会导致部分数据缺失,如商品价格缺失、销量缺失等。对于缺失值的处理,需要根据业务需求和数据特点选择合适的方法。
如果缺失值较少,可以直接删除包含缺失值的商品记录。例如,使用 Pandas 库的dropna方法:
import pandas as pd
df = pd.DataFrame(products)
df = df.dropna()
products = df.to_dict('records')
在上述代码中,首先将商品列表转换为 Pandas 的DataFrame对象,然后使用dropna方法删除包含缺失值的行,最后再将处理后的DataFrame转换回列表形式。
如果缺失值较多,直接删除可能会导致数据量大幅减少,影响数据分析的准确性。此时,可以使用均值、中位数或特定值填充缺失值。以价格字段为例,使用均值填充缺失值的代码如下:
import pandas as pd
df = pd.DataFrame(products)
mean_price = df['price'].mean()
df['price'] = df['price'].fillna(mean_price)
products = df.to_dict('records')
在这段代码中,先计算价格字段的均值mean_price,然后使用fillna方法将价格字段中的缺失值用均值填充 。同样,如果使用中位数填充,只需将mean_price替换为df[‘price’].median()即可。对于一些特定的业务场景,也可以使用特定值进行填充,如对于销量缺失值,可以填充为 0,表示销量未知但可认为是 0 。通过合理处理缺失值,可以提高数据的完整性和可用性,为后续的数据分析和挖掘提供更可靠的数据基础。
本次实战围绕电商网站动态网页爬取展开,重点攻克了登录验证、反爬虫机制以及动态商品信息爬取和数据清洗整理等关键环节。在登录验证方面,通过 Selenium 库模拟浏览器操作,成功绕过了复杂的滑块验证码;针对反爬虫机制,采用设置合理请求间隔、使用代理 IP 池和伪装请求头的策略,有效提高了爬虫的稳定性和成功率;在动态商品信息爬取中,借助 Selenium 模拟用户操作,实现了对动态加载商品数据的获取;最后,通过数据去重、格式规范化和缺失值处理等操作,对爬取到的数据进行了清洗与整理,提升了数据的质量和可用性。
然而,电商网站的数据爬取仍然面临诸多挑战,同时也蕴含着更多的探索空间。读者可以尝试对更多不同类型的电商网站进行爬取,进一步加深对不同网站反爬虫机制和页面结构的理解。在进阶技术方面,分布式爬虫是一个值得深入研究的方向。分布式爬虫通过将爬取任务分配到多个节点上并行处理,能够显著提高爬取效率,应对大规模数据爬取的需求。例如,可以使用 Scrapy - Redis 等分布式爬虫框架,结合 Redis 数据库实现任务分发和结果合并,充分发挥多台计算机的计算资源。
增量爬取也是一项具有重要应用价值的技术。它能够监测网站数据的更新,仅抓取新增或变化的数据,避免重复爬取,减少资源消耗。在实际应用中,可以通过记录上次爬取的时间戳或数据标识,对比新获取的数据,实现增量更新 。此外,随着人工智能技术的发展,将机器学习、深度学习算法应用于爬虫领域,如利用图像识别技术更智能地处理验证码,通过自然语言处理技术对爬取到的商品描述、用户评价等文本数据进行更深入的分析挖掘,也是未来的研究热点和发展趋势 。希望读者在掌握本次实战内容的基础上,积极探索这些进阶技术,不断提升自己在数据爬取和分析领域的能力。