使用Selenium模拟浏览器抓取淘宝商品美食信息(含模拟登陆)

本文最后更新于:2021年1月26日 晚上

淘宝页面比较复杂,含有各种请求参数和加密参数,如果直接请求或者分析Ajax将会非常繁琐。
Selenium是一个自动化测试工具,可以驱动浏览器去完成各种工作,比如模拟点击、输入和下拉等多种功能,这样我们只需关心操作,
不需要关心后台发生了怎么样的请求下面对具体操作步骤进行详述。

所用到的

  • selenium
  • pyqurey
  • re
  • pymongo

数据库截图

实现代码

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
212
213
214
215
216
217
218
219
220
221
222
223
224
# -*- coding: utf-8 -*-
"""
@Time : 2021/1/25 17:02
@Auth : Ne-21
@File :taobaospider.py
@IDE :PyCharm
@Motto:Another me.
"""
import re
from selenium.common.exceptions import TimeoutException
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
from selenium.webdriver import ActionChains
import time
from pyquery import PyQuery as pq
import pymongo

# Mongo数据库
MONGO_URL = 'localhost'
MONGO_DB = 'taobao'
MONGO_TABLE = 'product'
client = pymongo.MongoClient(MONGO_URL)
db = client[MONGO_DB]

options = webdriver.ChromeOptions()
# options.add_argument('--blink-settings=imagesEnabled=false') # 不加载图片, 提升速度
# options.add_argument('--headless') # 浏览器不提供可视化页面
browser = webdriver.Chrome(options=options)
# 防止被监测
browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": '''
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
'''
})
wait = WebDriverWait(browser, 5)


def do_slider():
"""
处理滑动验证码,没有测试
:return:
"""
slider_go = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#nc_1_n1z'))
)
# 实例化一个动作链关联游览器
action = ActionChains(browser)
action.reset_actions()
# 使用鼠标动作链进行点击并悬浮
action.click_and_hold(slider_go)
# 滑动验证码
action.move_by_offset(xoffset=258, yoffset=0).perform()
time.sleep(5)


def login(username, password):
print('正在登陆......')
browser.get('https://login.taobao.com/member/login.jhtml')
input_username = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#fm-login-id'))
)
input_password = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#fm-login-password'))
)
submit = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '#login-form > div.fm-btn > button'))
)
input_username.send_keys(username)
time.sleep(2)
input_password.send_keys(password)
time.sleep(2)
submit.click()
time.sleep(3) # 等待检验滑块

# 判断有无滑块验证
try:
slider = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#nc_1__scale_text > span'))
)
if bool(slider):
print('发现滑块验证码')
do_slider()
submit.click()
else:
print('未发现滑块')
pass
except:
print('未发现滑块')

time.sleep(5) # 等待短信验证
# 判断有无短信验证
try:
# 定位iframe标签,由于iframe没有id,name这种唯一的属性,因此只能通过先定位,在切换实现
frame = wait.until(
EC.presence_of_element_located((By.XPATH, '//*[@id="content"]/div/div[1]/iframe'))
)
# 由于手机验证页面出现了页面的嵌套,因此需要进行页面跳转到iframe下
browser.switch_to.frame(frame)
sms_button = wait.until(
EC.presence_of_element_located((By.XPATH, '//*[@id="J_GetCode"]'))
)
if bool(sms_button):
print('发现短信验证码')
time.sleep(1)
sms_button.click()
print('验证码已发送,请输入验证码(20s)')
time.sleep(20)
sms_submit = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '#submitBtn'))
)
sms_submit.click()
else:
print('无短信验证')
pass
except:
print('无短信验证...')
pass
finally:
print('登陆成功')


def search():
print('正在搜索.....')
try:
browser.get('https://www.taobao.com')
# 等待目标元素加载完成
# input = WebDriverWait(browser, 10).until(
# EC.presence_of_all_elements_located(By.CSS_SELECTOR, '#q')
# )
# submit = WebDriverWait(browser, 10).until(
# EC.element_to_be_clickable(By.CSS_SELECTOR, '#J_TSearchForm > div.search-button > button')
# )
# 改写WebDriverWait
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('美食')
submit.click()
# 获取总页数
total = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.total')))
# 第一页解析网页
get_products()
return total.text
except TimeoutException:
return search()


# 翻页操作
def next_page(page_number):
print('正在翻页......')
try:
# 到第几页
input = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > input'))
)
# 确定按钮
submit = wait.until(
EC.element_to_be_clickable(
(By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > span.btn.J_Submit'))
)
input.clear()
input.send_keys(page_number)
submit.click()
# 判断是否已翻到指定页面(*)
wait.until(EC.text_to_be_present_in_element(
(By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > ul > li.item.active > span'), str(page_number)))
# 翻页后解析网页
get_products()
except TimeoutException:
return next_page(page_number)


# 解析网页
def get_products():
# 商品信息是否存在
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-itemlist .items .item')))
# 拿到网页源代码
html = browser.page_source
# pyquery解析
doc = pq(html)
items = doc('#mainsrp-itemlist .items .item').items()
for item in items:
product = {
'image': item.find('.pic .img').attr('src'),
'price': item.find('.price').text(),
'deal': item.find('.deal-cnt').text()[:-3],
'title': item.find('.title').text(),
'shop': item.find('.shop').text(),
'location': item.find('.location').text()
}
print(product)
save_to_mongo(product)


def save_to_mongo(result):
print('正在加入数据库......')
try:
if db[MONGO_TABLE].insert(result):
print('存储到MONGODB成功', result)
except Exception:
print('存储异常', result)


def main():
print('开始运行.......')
login('', '')
total = search()
total = int(re.compile('(\d+)').search(total).group(1))
for i in range(2, total + 1):
next_page(i)
browser.close()


if __name__ == '__main__':
main()

补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
chrome_options.add_argument('--user-agent=""')  # 设置请求头的User-Agent
chrome_options.add_argument('--window-size=1280x1024') # 设置浏览器分辨率(窗口大小)
chrome_options.add_argument('--start-maximized') # 最大化运行(全屏窗口),不设置,取元素会报错
chrome_options.add_argument('--disable-infobars') # 禁用浏览器正在被自动化程序控制的提示
chrome_options.add_argument('--incognito') # 隐身模式(无痕模式)
chrome_options.add_argument('--hide-scrollbars') # 隐藏滚动条, 应对一些特殊页面
chrome_options.add_argument('--disable-javascript') # 禁用javascript
chrome_options.add_argument('--blink-settings=imagesEnabled=false') # 不加载图片, 提升速度
chrome_options.add_argument('--headless') # 浏览器不提供可视化页面

chrome_options.add_argument('--ignore-certificate-errors') # 禁用扩展插件并实现窗口最大化
chrome_options.add_argument('--disable-gpu') # 禁用GPU加速
chrome_options.add_argument('–disable-software-rasterizer')
chrome_options.add_argument('--disable-extensions')
chrome_options.add_argument('--start-maximized')