1.前言
这几天学习了Re库,正好可以用这个爬取淘宝的实例来加深一下印象,因为自己能力还不够,所以是跟着大佬们的脚步一起做的,对代码进行分析然后弄明白,在能力范围内也进行了一定的修改。
2.案例分析
先给出大佬的博客:
https://buwenbuhuo.blog.csdn.net/article/details/104987737
因为大佬写的不是很详细,所以我会按照自己的思路把每一行代码解释清楚
2.1.目标站点分析
我们要爬取的是淘宝网,url链接:https://www.taobao.com
查看robots协议,发现只有百度蜘蛛不能爬(吃瓜)

因为要爬取商品信息,所以我们在搜索栏随便输入一个商品名称,进入搜索结果一页,这里以红领巾为例,得到url地址:

取出文本:https://s.taobao.com/search?q=%E7%BA%A2%E9%A2%86%E5%B7%BE&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20210127&ie=utf8
多次输入商品名称就会发现,前面的search?q=都是固定的,=后面就是输入的商品,只不过浏览器自动对url地址的中文部分进行了url解码,单独取出后看到的是没有被解码的url地址
还要注意的是进行爬取时需要模拟浏览器登录,因为现在很多网站都是需要登录才能查看信息的,如果不登录搜索时会跳转到登录页面,所以需要我们在GET操作时加入header信息,需要包含cookie在内
本站也是没有反爬措施,所以不会太难
2.2.代码分析
首先我们根据代码的功能可以把完整代码分成四个区块,1.爬取页面,2.录入数据,3.数据输出,4.主函数调用子函数,代码分区可以让代码更加简洁易读,并且可以提高代码的复用性。
1.主函数
因为要有个大的框架,所以我们先写主函数,再写子函数,写完把主函数放在最后就好了。这里只用到了requests和re两个库。
主函数主要是调用函数加上基本的输入输出和搜索规则限制
这里我们给出主函数代码:
def main():
goods = input("请输入要爬取的商品:")
depth = eval(input("请输入爬取页数:")) # 爬取深度
start_url = 'https://s.taobao.com/search?q=' + goods
indolist = []
for i in range(depth):
try:
url = start_url + '&s=' + str(44 * i)
html = getHTMLText(url)
parsePage(indolist, html)
except:
continue
printGoodlist(indolist)
因为每一页的url地址都是不同的,我们取出前四页的url地址放在一起比较

可以看到每个地址最后都有&s=,而44恰好是一页商品的数量,在拼接url地址时要加上这个变量,因为地址中都是字符型,所以我们要加上str(),然后用+将元素拼接起来
同时,我们要将每一页都进行遍历,遍历的结果都存入indolist列表中,最后一次性输出,通常我们在遍历时要用try…except捕获异常,保证在出现异常时程序可以正常运行
2.爬取页面
跟普通的爬取不同的时,我们在这里要加入模拟浏览器信息,主要是user-agent和cookie,这些信息在登录页面可以查看,按F12进入开发者工具栏,按如下步骤查找



将User-Agent和cookie加入header中,注意要设置timeout时间,防止响应时间过长,代码如下:
def getHTMLText(url): # 获得页面函数
try:
header = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0',
'cookie': 'thw=cn; cna=npj6FSROsjACAdodv5gJhCng; hng=CN%7Czh-CN%7CCNY%7C156; tracknick=a459804692; _cc_=Vq8l%2BKCLiw%3D%3D; tg=0; enc=QBWbMOc0vQMNrTeNZMKkNSam6PhBuBNZFXZb7jRKj1R6%2FnZ%2FAztJW%2F4Ql%2BaqZxBArE4hIF2kV2dUg%2BBZSV88Eg%3D%3D; x=e%3D1%26p%3D*%26s%3D0%26c%3D0%26f%3D0%26g%3D0%26t%3D0%26__ll%3D-1%26_ato%3D0; miid=778994371391611536; t=c8d997f8c3862a9545d7aa8e0f55a811; _m_h5_tk=35adf942f203d5b2def2e96c38527acf_1579166355669; _m_h5_tk_enc=0f3e60d1c12cd628e261b932e21ab4a2; cookie2=15c929f24b3d18aec75687bbefabacb3; v=0; _tb_token_=e9b30e375eee7; alitrackid=www.taobao.com; lastalitrackid=www.taobao.com; __guid=154677242.3514116682747917000.1579155943921.0774; _uab_collina=157915745635971448621381; x5sec=7b227365617263686170703b32223a223365323561373037306538313737393931373032646330666464613337346335434b6154675045464549756a734e6e372f4a724f68674561444449314f5467344e5459344e6a55374d513d3d227d; JSESSIONID=391518D427C8B71F1754C4144A533BCA; monitor_count=8; isg=BE9PlXPQTWYkTEpDzc8TlxxO3uNZdKOWgEm-D2Fcj77tMG0yYUXr5h6mMmCOSHsO; l=cBa32tzuqwYH7YbhBOCwZuI8jO79hIRVguPRwnNXi_5dY18KvH_Oomns5eJ6cjWdtkLW4IFj7Ty9-etuiRHx3mx-g3fP.'
}
r = requests.get(url, headers=header, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
3.录入数据
首先对页面进行观察,我们使用第一个商品名称和价格,在网页源代码页面进行搜索

在源代码页面按ctrl+F即可调出搜索框,我们先对价格进行检索

可以看到价格为”view_price”:”3.20″,我们可以后续使用正则表达式进行匹配

名称方面,我们选取”raw_title”:”…”作为关键元素
然后我们使用正则表达式匹配这两个元素
plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"', html)
tlt = re.findall(r'\"raw_title\"\:\".*?\"', html) # 最小匹配
这两个正则表达式有个答主讲的很细致,https://zhidao.baidu.com/question/1695911847827365708.html
首先我们使用re.findall,它能返回一个列表,r’ ‘ 表示原生字符串,而在使用特殊符号时我们需要加上 ‘ \ ‘ 来进行标记,第二个正则表达式中 ‘ *? ‘ 是最小匹配,表示前一个字符0次或无限次扩展,而前一个字符是 ‘ . ‘ ,为任意单个字符,整体意思就是任意长度任意字符
在正则表达式匹配到响应元素后,我们使用.split函数将列表中每个值进行分隔,只取出后面的有效信息,再把两个信息一同存入新的列表,形成一个二维列表

4.数据输出
在输出的时候,我们先设定一个计时器,再对二维列表进行遍历,在遍历时每次让计时器+1作为输出结果的序号,代码如下:
def printGoodlist(ilt): # 输出结果信息到屏幕
tplt = "{:4}\t{:8}\t{:16}" #\t缩进
print(tplt.format("序号", "价格", "商品名称"))
count = 0
for g in ilt:
count = count + 1
print(tplt.format(count, g[0], g[1]))
在遍历的时候不使用两层for循环而直接print格式化输出,简化了代码
3.运行演示
完整代码如下:
import requests
import re
def getHTMLText(url): # 获得页面函数
try:
header = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0',
'cookie': 'thw=cn; cna=npj6FSROsjACAdodv5gJhCng; hng=CN%7Czh-CN%7CCNY%7C156; tracknick=a459804692; _cc_=Vq8l%2BKCLiw%3D%3D; tg=0; enc=QBWbMOc0vQMNrTeNZMKkNSam6PhBuBNZFXZb7jRKj1R6%2FnZ%2FAztJW%2F4Ql%2BaqZxBArE4hIF2kV2dUg%2BBZSV88Eg%3D%3D; x=e%3D1%26p%3D*%26s%3D0%26c%3D0%26f%3D0%26g%3D0%26t%3D0%26__ll%3D-1%26_ato%3D0; miid=778994371391611536; t=c8d997f8c3862a9545d7aa8e0f55a811; _m_h5_tk=35adf942f203d5b2def2e96c38527acf_1579166355669; _m_h5_tk_enc=0f3e60d1c12cd628e261b932e21ab4a2; cookie2=15c929f24b3d18aec75687bbefabacb3; v=0; _tb_token_=e9b30e375eee7; alitrackid=www.taobao.com; lastalitrackid=www.taobao.com; __guid=154677242.3514116682747917000.1579155943921.0774; _uab_collina=157915745635971448621381; x5sec=7b227365617263686170703b32223a223365323561373037306538313737393931373032646330666464613337346335434b6154675045464549756a734e6e372f4a724f68674561444449314f5467344e5459344e6a55374d513d3d227d; JSESSIONID=391518D427C8B71F1754C4144A533BCA; monitor_count=8; isg=BE9PlXPQTWYkTEpDzc8TlxxO3uNZdKOWgEm-D2Fcj77tMG0yYUXr5h6mMmCOSHsO; l=cBa32tzuqwYH7YbhBOCwZuI8jO79hIRVguPRwnNXi_5dY18KvH_Oomns5eJ6cjWdtkLW4IFj7Ty9-etuiRHx3mx-g3fP.'
}
r = requests.get(url, headers=header, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
def parsePage(ilt, html): # 解析获得的页面
try:
plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"', html)
tlt = re.findall(r'\"raw_title\"\:\".*?\"', html) #最小匹配
for i in range(len(plt)):
price = eval(plt[i].split(':')[1]) #将plt的第i个元素取出,利用:划分为列表,取出第二个元素,获得:后面的有效信息
title = eval(tlt[i].split(':')[1])
ilt.append([price, title]) #一个元素中有两个值,二维列表
except:
print("")
def printGoodlist(ilt): # 输出结果信息到屏幕
tplt = "{:4}\t{:8}\t{:16}" #\t缩进
print(tplt.format("序号", "价格", "商品名称"))
count = 0
for g in ilt:
count = count + 1
print(tplt.format(count, g[0], g[1]))
def main(): # 主函数
goods = input("请输入要爬取的商品:")
depth = eval(input("请输入爬取页数:")) # 爬取深度
start_url = 'https://s.taobao.com/search?q=' + goods
indolist = [] # 输出结果
for i in range(depth):
try:
url = start_url + '&s=' + str(44 * i)
html = getHTMLText(url)
parsePage(indolist, html)
except:
continue
printGoodlist(indolist)
main()
后续还可以对它进行优化,比如对金额进行判断,差别过大的数据直接舍去,还可以进行数据可视化等
需要注意的是cookie是有期限的,需要加入自己的cookie才行
运行结果如下:
