淘宝爬虫-Python实践

本文仅用于交流学习,分享介绍相关技术

Python爬虫

Python爬虫是一种自动化技术,旨在模拟人类访问网页并从中提取所需数据。通过爬虫,可以批量采集网页中的信息,如文本、图片、视频等,为数据分析、市场调研和内容聚合等应用提供支持。Python因其简单易学的语法和强大的第三方库,成为开发爬虫的首选语言。

以简单的示例为例,使用requests库可以轻松发送请求并获取网页响应,而BeautifulSoup则帮助解析网页内容并提取目标数据,例如提取所有标题文本后将其保存到文件中。尽管爬虫技术非常强大,但使用时需注意合法性与道德性,需遵守目标网站的robots.txt协议,并避免过于频繁的请求。此外,面对目标网站的反爬机制,可以通过添加代理IP、伪装请求头或随机化请求间隔等方式进行适当应对。Python爬虫广泛应用于电商比价、学术研究、内容聚合等领域,其高效性和灵活性使其成为现代数据获取的重要工具之一。

淘宝爬虫代码解析

爬虫使用 Selenium 和 PyQuery 库从淘宝网站上爬取商品信息。该脚本主要功能包括搜索商品、翻页、获取商品信息并将其保存到 Excel 表格中。以下是对代码功能的详细分块介绍。

导入库和全局变量

1
2
3
4
5
6
7
8
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from pyquery import PyQuery as pq
import time
import openpyxl as op

全局变量

此处根据自己所要爬取的商品名和需要爬取的数据量自行修改

1
2
3
4
count = 1   # 爬取的数据计数
KEYWORD = "婴儿奶粉" # 搜索关键词
pageStart = 1 # 爬取起始页
pageEnd = 30 #爬取结束页

初始化 WebDriver (使用Edge)

此处打开页面后会暂停15s,以便进行扫码登录操作。

1
2
3
4
5
6
7
8
9
options = webdriver.EdgeOptions()
options.use_chromium = True
options.add_experimental_option("excludeSwitches", ['enable-automation'])
driver = webdriver.Edge(options=options)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",
{"source": """Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"""} )
driver.get('https://www.taobao.com')
driver.maximize_window()
wait = WebDriverWait(driver, 15)

搜索商品

1
2
3
4
5
6
7
8
9
10
11
def search_goods(KEYWORD):
try:
print("正在搜索: {}".format(KEYWORD))
input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#q")))
submit = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_TSearchForm > div.search-button > button')))
input.send_keys(KEYWORD)
submit.click()
time.sleep(2)
print("搜索完成!")
except Exception as exc:
print("search_goods函数错误!")

翻页到指定的 pageStart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def turn_pageStart():
try:
print("正在翻转:第{}页".format(pageStart))
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(3)
pageInput = wait.until(EC.presence_of_element_located(
(By.XPATH, '//*[@id="search-content-leftWrap"]/div[2]/div[4]/div/div/span[3]/input')))
pageInput.send_keys(pageStart)
admit = wait.until(EC.element_to_be_clickable(
(By.XPATH, '//*[@id="search-content-leftWrap"]/div[2]/div[4]/div/div/button[3]')))
admit.click()
print("已翻至:第{}页".format(pageStart))
except Exception as exc:
print("turn_pageStart函数错误!")

获取商品信息

对页面信息进行爬取前需要手动确认商品已经全部加载完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def get_goods(page):
try:
global count
if input('确认界面加载完毕,输入数字“1”开始爬取-->') == 1:
pass
html = driver.page_source
doc = pq(html)
items = doc('div.content--CUnfXXxv > div > div').items()
for item in items:
title = item.find('.title--qJ7Xg_90 span').text()
price_int = item.find('.priceInt--yqqZMJ5a').text()
price_float = item.find('.priceFloat--XpixvyQ1').text()
if price_int and price_float:
price = float(f"{price_int}{price_float}")
else:
price = 0.0
deal = item.find('.realSales--XZJiepmt').text()
location = item.find('.procity--wlcT2xH9 span').text()
shop = item.find('.shopNameText--DmtlsDKm').text()
postText = item.find('.subIconWrapper--Vl8zAdQn').text()
postText = "包邮" if "包邮" in postText else "/"
t_url = item.find('.doubleCardWrapperAdapt--mEcC7olq').attr('href')
shop_url = item.find('.TextAndPic--grkZAtsC a').attr('href')
img_url = item.find('.mainPicAdaptWrapper--V_ayd2hD img').attr('src')
style_list = item('div.abstractWrapper--whLX5va5 > div').items()
style = [s('div.descBox--RunOO4S3 > span').text() for s in style_list if s('div.descBox--RunOO4S3 > span').text() != '']
product = {
'Page': page,
'Num': count-1,
'title': title,
'price': price,
'deal': deal,
'location': location,
'shop': shop,
'isPostFree': postText,
'url': t_url,
'shop_url': shop_url,
'img_url': img_url
}
print(product)
wb.cell(row=count, column=1, value=page)
wb.cell(row=count, column=2, value=count-1)
wb.cell(row=count, column=3, value=title)
wb.cell(row=count, column=4, value=price)
wb.cell(row=count, column=5, value=deal)
wb.cell(row=count, column=6, value=location)
wb.cell(row=count, column=7, value=shop)
wb.cell(row=count, column=8, value=postText)
wb.cell(row=count, column=9, value=t_url)
wb.cell(row=count, column=10, value=shop_url)
wb.cell(row=count, column=11, value=img_url)
for i in range(0, len(style)):
wb.cell(row=count, column=12+i, value=style[i])
count += 1
except Exception:
print("get_goods函数错误!")

翻页函数

同样的,进行翻页时请确认页面保持在页面底部,可以看见“下一页”按钮,否则翻页函数可能报错(此时可以手动翻页继续爬取)。

1
2
3
4
5
6
7
8
9
10
def page_turning(page_number):
try:
print(f"正在翻页: 第{page_number}页")
time.sleep(2)
submit = wait.until(EC.element_to_be_clickable((By.XPATH, '//span[@class="next-btn-helper" and contains(text(), "下一页")]')))
driver.execute_script("arguments[0].click();", submit)
wait.until(EC.text_to_be_present_in_element((By.XPATH, '//*[@id="search-content-leftWrap"]/div[2]/div[4]/div/div/span[1]/em'), str(page_number)))
print(f"已翻至: 第{page_number}页")
except Exception as exc:
print(f"page_turning函数错误: {str(exc)}")

主函数部分

1
2
3
4
5
6
7
8
9
10
11
def Crawer_main():
try:
search_goods(KEYWORD)
if pageStart != 1:
turn_pageStart()
get_goods(pageStart)
for i in range(pageStart + 1, pageEnd+1):
page_turning(i)
get_goods(i)
except Exception as exc:
print("Crawer_main函数错误!")

程序入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if __name__ == '__main__':
try:
ws = op.Workbook()
wb = ws.create_sheet(index=0)
title_list = ['Page', 'Num', 'title', 'Price', 'Deal', 'Location', 'Shop', 'IsPostFree', 'Title_URL', 'Shop_URL', 'Img_URL', 'Style_1', 'Style_2', 'Style_3']
for i in range(0, len(title_list)):
wb.cell(row=count, column=i + 1, value=title_list[i])
count += 1
except Exception as exc:
print("Excel建立失败!")
Crawer_main()
data = time.strftime('%Y%m%d-%H%M', time.localtime(time.time()))
Filename = "{}_No.{}~{}_{}_FromTB.xlsx".format(KEYWORD, pageStart, pageEnd, data)
ws.save(filename = Filename)
print(Filename + "存储成功")

总结

该脚本通过 Selenium 控制 Edge 浏览器,模拟用户在淘宝网站上的搜索和翻页操作,并使用 PyQuery 解析网页内容,提取商品信息,最终将数据保存到 Excel 表格中。

主要功能

  • 搜索商品:通过输入关键词并点击搜索按钮,脚本能够在淘宝网站上搜索指定的商品。
  • 翻页操作:脚本能够自动翻页,从起始页到终止页,确保所有指定页码的商品信息都被爬取。
  • 获取商品信息:脚本能够提取商品的标题、价格、交易量、所在地、店铺名称、是否包邮、商品链接、店铺链接、图片链接以及商品风格等信息。
  • 保存数据:所有提取到的商品信息都会被保存到一个 Excel 表格中,方便后续的数据分析和处理。

使用方法

  • 安装依赖:确保已安装 Selenium、PyQuery 和 openpyxl 库。
  • 配置 WebDriver:下载并配置适用于 Edge 浏览器的 WebDriver。
  • 运行脚本:在命令行中运行脚本,脚本会自动打开浏览器并开始爬取数据。
  • 手动操作:在某些情况下,可能需要手动完成滑块验证。
  • 查看结果:脚本运行结束后,会在当前目录下生成一个包含爬取数据的 Excel 文件。

注意事项

  • 反爬机制:淘宝网站有一定的反爬机制,脚本中已包含一些反爬措施,但在实际运行中可能仍会遇到问题。
  • 手动干预:在某些情况下,可能需要手动翻页和确认页面正常加载。
  • 数据准确性:由于网页结构可能会发生变化,脚本中的选择器可能需要根据实际情况进行调整。

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# 声明第三方库/头文件
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from pyquery import PyQuery as pq
import time
import openpyxl as op #导入Excel读写库

# 全局变量
count = 1 # 写入Excel商品计数
KEYWORD = "婴儿奶粉" # 要搜索的商品的关键词
pageStart = 1 # 爬取起始页
pageEnd = 30 # 爬取终止页

# 启动EdgeDriver服务
options = webdriver.EdgeOptions()
options.use_chromium = True # 确保使用的是 Chromium 版本的 Edge
# 关闭自动测试状态显示 // 会导致浏览器报:请停用开发者模式
options.add_experimental_option("excludeSwitches", ['enable-automation'])
# 把Edge设为selenium驱动的浏览器代理;
driver = webdriver.Edge(options=options)
# 反爬机制
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",
{"source": """Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"""})
driver.get('https://www.taobao.com')
# 窗口最大化
driver.maximize_window()
# wait是Selenium中的一个等待类,用于在特定条件满足之前等待一定的时间。
# 如果一直到等待时间都没满足则会捕获TimeoutException异常
wait = WebDriverWait(driver,15)

# 输入“关键词”,搜索
def search_goods(KEYWORD):
try:
print("正在搜索: {}".format(KEYWORD))
# 找到搜索“输入框”
input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#q")))
# 找到“搜索”按钮
submit = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_TSearchForm > div.search-button > button')))
# 输入框写入“关键词KeyWord”
input.send_keys(KEYWORD)
# 点击“搜索”按键
submit.click()
# 搜索商品后会再强制停止2秒,如有滑块手动操作
time.sleep(2)
print("搜索完成!")
except Exception as exc:
print("search_goods函数错误!")

# 翻页至第pageStar页
def turn_pageStart():
try:
print("正在翻转:第{}页".format(pageStart))
# 滑动到页面底端
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# 滑动到底部后停留3s
time.sleep(3)
# 找到输入“页面”的表单,输入“起始页”
pageInput = wait.until(EC.presence_of_element_located(
(By.XPATH, '//*[@id="search-content-leftWrap"]/div[2]/div[4]/div/div/span[3]/input')))
pageInput.send_keys(pageStart)
# 找到页面跳转的“确定”按钮,并且点击
admit = wait.until(EC.element_to_be_clickable(
(By.XPATH, '//*[@id="search-content-leftWrap"]/div[2]/div[4]/div/div/button[3]')))
admit.click()
print("已翻至:第{}页".format(pageStart))
except Exception as exc:
print("turn_pageStart函数错误!")

# 获取每一页的商品信息;
def get_goods(page):
try:
# 声明全局变量count
global count
if input('确认界面加载完毕,输入数字“1”开始爬取-->') == 1:
pass
# 获取html网页
html = driver.page_source
doc = pq(html)
# 提取所有商品的共同父元素的类选择器
items = doc('div.content--CUnfXXxv > div > div').items()
for item in items:
# 定位商品标题
title = item.find('.title--qJ7Xg_90 span').text()
# 定位价格
price_int = item.find('.priceInt--yqqZMJ5a').text()
price_float = item.find('.priceFloat--XpixvyQ1').text()
if price_int and price_float:
price = float(f"{price_int}{price_float}")
else:
price = 0.0
# 定位交易量
deal = item.find('.realSales--XZJiepmt').text()
# 定位所在地信息
location = item.find('.procity--wlcT2xH9 span').text()
# 定位店名
shop = item.find('.shopNameText--DmtlsDKm').text()
# 定位包邮的位置
postText = item.find('.subIconWrapper--Vl8zAdQn').text()
postText = "包邮" if "包邮" in postText else "/"
# 定位商品url
t_url = item.find('.doubleCardWrapperAdapt--mEcC7olq')
t_url = t_url.attr('href')
# t_url = item.attr('a.doubleCardWrapperAdapt--mEcC7olq href')
# 定位店名url
shop_url = item.find('.TextAndPic--grkZAtsC a')
shop_url = shop_url.attr('href')
# 定位商品图片url
img = item.find('.mainPicAdaptWrapper--V_ayd2hD img')
img_url = img.attr('src')
# 定位风格
style_list = item('div.abstractWrapper--whLX5va5 > div').items()
style = []
for s in style_list:
s_span = s('div.descBox--RunOO4S3 > span').text()
if s_span != '':
style.append(s_span)

# 构建商品信息字典
product = {
'Page': page,
'Num': count-1,
'title': title,
'price': price,
'deal': deal,
'location': location,
'shop': shop,
'isPostFree': postText,
'url': t_url,
'shop_url': shop_url,
'img_url': img_url
}
print(product)
# 商品信息写入Excel表格中
wb.cell(row=count, column=1, value=page) # 页码
wb.cell(row=count, column=2, value=count-1) # 序号
wb.cell(row=count, column=3, value=title) # 标题
wb.cell(row=count, column=4, value=price) # 价格
wb.cell(row=count, column=5, value=deal) # 付款人数
wb.cell(row=count, column=6, value=location) # 地理位置
wb.cell(row=count, column=7, value=shop) # 店铺名称
wb.cell(row=count, column=8, value=postText) # 是否包邮
wb.cell(row=count, column=9, value=t_url) # 商品链接
wb.cell(row=count, column=10, value=shop_url) # 商铺链接
wb.cell(row=count, column=11, value=img_url) # 图片链接
for i in range(0,len(style)):
wb.cell(row=count, column=12+i, value=style[i]) # 风格1~3
count += 1 # 下一行
except Exception:
print("get_goods函数错误!")

# 翻页
def page_turning(page_number):
try:
print(f"正在翻页: 第{page_number}页")
time.sleep(2) # 等待页面加载
# 使用新的 XPath 定位下一页按钮
submit = wait.until(EC.element_to_be_clickable((By.XPATH, '//span[@class="next-btn-helper" and contains(text(), "下一页")]')))
# 强制点击
driver.execute_script("arguments[0].click();", submit)
# 等待当前页码更新
wait.until(EC.text_to_be_present_in_element((By.XPATH, '//*[@id="search-content-leftWrap"]/div[2]/div[4]/div/div/span[1]/em'), str(page_number)))
print(f"已翻至: 第{page_number}页")
except Exception as exc:
print(f"page_turning函数错误: {str(exc)}")


# 爬虫main函数
def Crawer_main():
try:
# 搜索KEYWORD
search_goods(KEYWORD)
# 判断pageStart是否为第1页
if pageStart != 1:
turn_pageStart()
# 爬取PageStart的商品信息
get_goods(pageStart)
# 从PageStart+1爬取到PageEnd
for i in range(pageStart + 1, pageEnd+1):
page_turning(i)
get_goods(i)
except Exception as exc:
print("Crawer_main函数错误!")



if __name__ == '__main__':
# 建立Excel表格
try:
ws = op.Workbook() # 创建Workbook
wb = ws.create_sheet(index=0) # 创建worsheet
# Excel第一行:表头
title_list = ['Page', 'Num', 'title', 'Price', 'Deal', 'Location', 'Shop', 'IsPostFree', 'Title_URL',
'Shop_URL', 'Img_URL', 'Style_1', 'Style_2', 'Style_3']
for i in range(0, len(title_list)):
wb.cell(row=count, column=i + 1, value=title_list[i])
count += 1 # 从第二行开始写爬取数据
except Exception as exc:
print("Excel建立失败!")

# 开始爬取数据
Crawer_main()

# 保存Excel表格
data = time.strftime('%Y%m%d-%H%M', time.localtime(time.time()))
Filename = "{}_No.{}~{}_{}_FromTB.xlsx".format(KEYWORD,pageStart,pageEnd,data)
ws.save(filename = Filename)
print(Filename + "存储成功")