selenium淘宝爬虫
- 使用selenium做淘宝商品爬虫
-
- 1、准备工作
- 2、页面分析
- 3、代码实现
-
- 3-1、模拟登陆
- 3-2、商品列表页
- 3-3、获取商品信息
- 3-4、数据库设计
- 3-5、爬虫执行
- 3-6、爬虫执行结果
- 4、待解决的问题
- 5、总结
使用selenium做淘宝商品爬虫
最近在学习崔庆才老师的《Python3网络爬虫开发实战》,第七章《动态渲染页面爬取》的使用selenium爬取淘宝商品,由于该书出版时间已久,淘宝的反爬机制做了其他改动,书中代码需要改进,特发此文,记录自己学习过程中遇到的问题及解决方法。
1、准备工作
确认已经安装chrome浏览器,并配置好对应版本的ChromeDriver,另外还需要正确安装selenium。
ChromeDriver各版本可在 ChromeDriver各版本下载 中查找并下载。下载完解压到你Anaconda安装目录下的Scripts目录即可。
2、页面分析
打开 淘宝主页 在搜索框键入你想要搜索的商品,进入商品列表页。比如搜“辣条”,得到以下页面:
每个商品包含该商品的基本信息,比如价格、图片、名称、店铺信息等,我们的目的就是将它们爬取下来。
拉到页面底部,会出现如下分页导航,我们爬取多页商品时,翻页需要用到它。
3、代码实现
这部分是整个项目的代码实现阶段。
3-1、模拟登陆
在学习书中代码时,书中第一步就是直接到商品列表页,但是仙子啊已经行不通了,因为淘宝改成了需要登陆账号才能访问商品列表页,因此需要模拟登陆淘宝。
首先创建account.py文件,保存自己的账号信息。之后就可以编辑登陆函数来实现登陆操作了。但是实际过程并没有这么简单,比如我遇到了以下问题。
出现滑块验证,并且无论我怎么滑,都无法通过验证。上网查询解决方案,理由是淘宝检测出了我们浏览器是通过selenium打开的,因此无法访问。selenium打开的浏览器有一个特征,就是window.navigator.webdriver的值为true,而正常打开的浏览器,该值是undifine。修改webdriver为undifine试试。
根据 Chrome 79以后版本Selenium中window.navigator.webdriver 值无法更改的解决方法 我们插入以下代码:
options = ChromeOptions()
# 开启实验性功能
options.add_argument('--start-maximized') # 最大化运行(全屏窗口),不设置,取元素会报错
options.add_experimental_option('excludeSwitches', ['enable-automation'])
options.add_experimental_option('useAutomationExtension', False)
browser = webdriver.Chrome(options=options)
script = '''
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
'''
browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": script})
wait = WebDriverWait(browser, 5)
试试运行结果:
遗憾失败,结果与未插入一样。可能是chrome版本升级带来的问题,试试查找chrome88.0的解决方法。
在 chrome88.0版本修改selenium的window.navigator.webdriver 文章中找到可行的解决方法。插入以下代码:
运行试试结果:
达到目的。但是下一步也存在问题,登陆完,有个验证码拦截页面。离谱,第一天运行还没有呢,第二天就出现了。
不过这个拦截比较简单,手动验证即可,验证完直接到达商品列表页。
本节代码如下:
KEYWORD = '辣条'
USER = account.USER
PASSWORD = account.PASSWORD
options = ChromeOptions()
# 开启实验性功能
options.add_argument('--start-maximized') # 最大化运行(全屏窗口),不设置,取元素会报错
options.add_experimental_option('excludeSwitches', ['enable-automation'])
options.add_experimental_option('useAutomationExtension', False)
options.add_argument("--disable-blink-features=AutomationControlled")
browser = webdriver.Chrome(options=options)
wait = WebDriverWait(browser, 5)
def login():
url = 'https://s.taobao.com/search?q=' + quote(KEYWORD)
browser.get(url)
# 登录
user = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#fm-login-id')))
user.send_keys(USER)
password = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#fm-login-password')))
password.send_keys(PASSWORD)
submit = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.fm-btn > button')))
submit.click()
time.sleep(1)
3-2、商品列表页
到达商品列表页后的操作,书上的代码并没有任何问题这里不再缀述,代码如下:
def index_page(page):
'''
:param page: 页码信息
:return:
'''
try:
# 翻页
if page > 1:
page_input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager div.form > input')))
page_submit = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager div.form > span.btn.J_Submit')))
page_input.clear()
page_input.send_keys(page)
page_submit.click()
time.sleep(3)
# 判定是否成功翻页
if wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#mainsrp-pager ul.items li.item.active > span'), str(page))):
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-itemlist div.items .item')))
get_products(page)
except TimeoutException:
index_page(page)
值得一提的是,这里的翻页操作是直接在分页列表的额输入框输入页码进行翻页的。
3-3、获取商品信息
这一步是提取每个商品的信息并保存到数据库,书本上用的是MongoDB,我用的是MySQL。
def get_products(page):
html = browser.page_source
doc = pq(html)
items = doc('#mainsrp-itemlist div.items .item').items()
items = list(items)
# print(len(items))
if page < 10:
page = '0' + str(page)
for i in range(len(items)):
products = {
'id': datetime.now().strftime('%Y%m%d') + page + str(i+1),
'title': items[i].find('.title').text(),
'price': items[i].find('.price').text(),
'deal': items[i].find('.deal-cnt').text(),
'shop': items[i].find('.shop').text(),
'location': items[i].find('.location').text(),
'image': items[i].find('.pic .img').attr('data-src'),
}
# print(products)
save_mysql.insert_data(data=products, dbname=DBNAME, table=KEYWORD)
提取出来的商品信息是没有id的,我在这里利用时间、页码和商品索引给它们设计了一个id,方便查询。
3-4、数据库设计
本节是储存商品的数据库设计。
import pymysql
import account
MYPASSWORD = account.MYSQL_PASSWORD
def create_db(dbname):
'''
创建数据库
:param dbname: 数据库名
:return:
'''
db = pymysql.connect(host='localhost', user='root', passwd=MYPASSWORD, port=3306)
cursor = db.cursor()
sql = 'CREATE DATABASE {dbname} DEFAULT CHARACTER SET utf8'.format(dbname=dbname)
cursor.execute(sql)
db.close()
def create_table(dbname, table_name):
'''
创建表
:param dbname: 数据库名
:param table_name: 数据表名
:return:
'''
db = pymysql.connect(host='localhost', user='root', passwd=MYPASSWORD, port=3306, db=dbname)
cursor = db.cursor()
sql = 'CREATE TABLE IF NOT EXISTS {table_name} (id VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL, price VARCHAR(255) NOT NULL, deal VARCHAR(255), shop VARCHAR(255) NOT NULL, location VARCHAR(255), image VARCHAR(255) NOT NULL, PRIMARY KEY(id))'.format(table_name=table_name)
cursor.execute(sql)
db.close()
def insert_data(data, dbname, table):
'''
插入数据
:param data: 数据
:param dbname: 数据库
:param table: 数据标
:return:
'''
db = pymysql.connect(host='localhost', user='root', passwd=MYPASSWORD, port=3306, db=dbname)
cursor = db.cursor()
keys = ','.join(data.keys())
values = ','.join(['%s'] * len(data))
sql = 'INSERT INTO {table}({keys}) VALUES ({values})'.format(table=table, keys=keys, values=values)
try:
if cursor.execute(sql, tuple(data.values())):
# print('Done')
db.commit()
except:
print(pymysql.DatabaseError.with_traceback())
db.rollback()
db.close()
3-5、爬虫执行
为了不给淘宝网站成太大负载,这里就只爬取两页。
if __name__ == '__main__':
# max_page = int(input('请输入你想要爬取的页数:'))
login()
max_page = 2
for page in range(1, max_page+1):
index_page(page=page)
time.sleep(2)
3-6、爬虫执行结果
爬虫会自动登录,手动验证完后,会自动执行爬取和翻页操作。
打开数据库,查看数据是否保存。
成功保存。
4、待解决的问题
其实在这个项目中我遇到的问题不止这么点,我还遇到了验证码拦截界面死活验证不成功的,然后参考了 selenium 反爬虫之跳过淘宝滑块验证功能的实现代码 这篇文章,修改了chromewebdriver.exe的**$cdc_asdjflasutopfhvcZLmcfl_**参数,才能验证成功。但是离谱的是,为了写这篇博客,我换回了初始的chromewebdriver.exe,想以此引出我遇到的问题,结果居然能验证成功了,离谱。希望路过的大牛能帮我解答一二。
还有另外的问题,就是为了不手动验证,我加入了自动破解滑块验证码的功能,但是一直验证失败。可能是我自动滑块的功能被识别出是机器人吧,怎么调参都验证失败。一下是我的破解代码,希望路过的大牛能教我怎么设置参数才能让爬虫表现的像个人。
def get_track(distance):
track = []
current = 0
t = 3
mid = distance * (7/8)
v0 = 0
a_list = [10, 6, 5, 8]
while current < distance:
if current < mid:
a = random.choice(a_list)
else:
a = -10
v = v0 + a * t
X = v0 * t + 0.5 * a * (t**2)
v0 = v
current += X
track.append(round(X))
return track
def move_slider():
time.sleep(3)
slider = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#nc_1_n1z')))
slider_bg = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#nc_1__scale_text')))
# time.sleep(2)
size = slider.size
bg_size = slider_bg.size
distance = bg_size['width'] - size['width']
tracks = get_track(distance)
ActionChains(browser).click_and_hold(slider).perform()
for x in tracks:
ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()
ActionChains(browser).release().perform()
5、总结
本次实验是一个不错的爬虫实验,熟练使用selenium是爬虫工程师必备的技能。希望这篇文章可以对小白有点帮助,也希望路过的大牛能帮忙解决一下我遇到的问题,学无止境,你我共勉。