年初自学python一段时间,项目中很少遇到需要实践python的地方,最近公司机顶盒的应用市场需要进行应用挂网,测试同事需要从当贝市场和奇珀市场下载特定的200多个应用,并提取信息挂到自己的服务器上,作为客户端开发的我,能用程序实现的绝对不会使用重复劳动力。
安装python 3.7,针对爬虫程序依赖的module,以管理员身份启动cmd
所需模块:beautifulsoup4 处理网页解析
requests 处理网页请求
re 处理正则表达式
lxml 是python的一个解析库,支持HTML和XML的解析
lxml需要自己下载:https://www.lfd.uci.edu/~gohlke/pythonlibs/
使用pip install <模块>即可。
如下图,需要抓取应用名称,版本,简介和图片并下载。
chrome F12后可以看到相应标签的class等属性,右键-copy-copy selector就可以复制css选择器标签元素。
关键代码如下:
def downloadApp(AppName):
url = 'http://www.dangbei.com/app/plus/search.php?kwtype=0&q=' + AppName
# 获取网页源码
web_data = requests.get(url, headers=headers)
# print('content:' + web_data.text)
# 解析网页
soup = BeautifulSoup(web_data.text, 'lxml')
try:
# 获取第一个搜索内容的名字
firstRecordName = soup.select('#softList > li:nth-of-type(1) > div > div.softInfo > p.title > a')[
0].get_text().strip()
if AppName in firstRecordName:
# 正则表达式找出抬头与红色关键词之间的跳转链接文本
# 正则表达式的运用可以参考这篇博客: https://www.cnblogs.com/chuxiuhong/p/5885073.html
pattern0 = re.compile(r'<a href=".+' + "<font color='red'>")
linkUrls = pattern0.findall(web_data.text)
# print(linkUrls[0])
# 获取二级页面链接 http://www.dangbei.com/app/tv/2015/0723/2687.html变为https://m.dangbei.com/wap-view-2687.html
tempStr = linkUrls[0].split()[1].split('"')[1]
tempList = re.findall(r'\d+', tempStr)
linkUrl = 'https://m.dangbei.com/wap-view-' + tempList[2] + ".html"
print("'" + firstRecordName + "'" + "的应用页面:" + linkUrl)
# 进入二级页面,以获取应用详情
web_data = requests.get(linkUrl, headers=headers)
soup = BeautifulSoup(web_data.text, 'lxml')
try:
version = soup.select('div.app-cat > span:nth-of-type(4)')[0].get_text().strip()
version = version.split(':')[1]
print("'" + firstRecordName + "'" + "的版本号:\n" + version)
filename = AppName + '_' + version
appdir = os.path.join(PATH, filename)
isExists = os.path.exists(appdir)
if not isExists:
# 创建文件夹
print('新建了一个', filename, '的文件夹!')
os.makedirs(appdir)
# 切换工作路径到AppName下
os.chdir(appdir)
else:
print(filename, '文件夹已经存在了!')
# if shutil.rmtree(appdir, True):
# os.rmdir(appdir) # 只能删除空目录,否则报错
os.chdir(appdir)
return
introduction = soup.select('div.info-content > div > p')[0].get_text()
print("'" + firstRecordName + "'" + "的详情介绍:\n" + introduction)
# 'w'或者'wb'表示写文本文件或写二进制文件
with open(filename + '.txt', 'w', encoding='utf-8') as f:
f.write(introduction)
photourl = soup.select('div.scroll_box > ul > li')
print("'" + firstRecordName + "'" + "的图片地址:")
count = 1
for temp in photourl:
temp_url = temp.find('img')['src']
temp_url = 'http://' + temp_url.split('//')[1]
large_url = temp_url.split('!')[0]
print(large_url)
urllib.request.urlretrieve(large_url, 'large_%s.jpg' % count)
urllib.request.urlretrieve(temp_url, 'small_%s.jpg' % count)
count += 1
apk = soup.select('div.info_downxload > a.btn_download_app')[0]
apkurl = apk.attrs['href']
apktitle = apk.attrs['title']
print("'" + firstRecordName + "'" + "的下载链接:\n" + apkurl)
urllib.request.urlretrieve(apkurl, apktitle + '_' + version + '.apk')
except:
print('search error')
os.chdir(PATH)
with open('record.txt', 'a+', encoding='utf-8') as f:
f.write(AppName + '\n')
else:
print("未找到与'" + AppName + "'相关的内容")
os.chdir(PATH)
with open('record.txt', 'a+', encoding='utf-8') as f:
f.write(AppName + '\n')
except:
print("未找到与'" + AppName + "'相关的内容")
os.chdir(PATH)
with open('record.txt', 'a+', encoding='utf-8') as f:
f.write(AppName + '\n')
抓取结果如图:
大致使用:applist.txt输入需要抓取的应用名称,若有搜索结果便新建一个应用文件夹,里面存放图片和apk等信息,由于抓取的信息需要存放在对应目录,采用线程池并行下载会存在文件存放错乱的问题,所以采用串行下载。
期间遇到的问题:
- 复制的selector的若包含nth-child,得到的结果是空,使用nth-of-type即可。两者区别: nth-of-type(n) 匹配属于父元素的特定类型的第 N 个子元素的每个元素,nth-child(n)选取父元素的第 N 个子元素,与类型无关。
- os.rmdir只能删除空目录,否则报错;shutil.rmtree() 可递归删除文件夹下的所有子文件夹和子文件。
- 读写文件,记得统一编码,比如encoding=‘utf-8’。