本次实验目标为爬取Apple官网的中国大陆中的官网专卖店具体位置以及所属城市或地区等信息,并将专卖店的图片保存至本地。此外,对每座城市中的专卖店数量进行统计,并计算中国区所有专卖店的数量,最后进行相关数据的可视化。
(1)语言:Python
(2)编译器:Pycharm
(3)使用模块:
①requests(请求到网页从而获取页面源代码)
②BeautifulSoup(对页面源代码进行解析,将文档转化为树形结构)
③webdriver(控制浏览器,为图片的保存提供前提)
④ActionChains(实现自动化点击鼠标右键)
⑤pyautogui(模拟键盘操作从而完成对图片的保存操作)
⑥time(适当延时)
本次Python程序主要分为四个部分——获取页面源代码并进行解析、查找到所需信息并进行输出、查找到对应专卖店图片并保存至本地、数据可视化。
(1)获取页面源代码并进行解析
将Apple官网的中国大陆的专卖店官网网址赋给url,再通过text = requests.get(url).text用requests模拟浏览器向服务器发送 Http 请求,并把源代码存到text。再通过main_page = BeautifulSoup(text, "html.parser"),使用BeautifulSoup进行解析。
为了防止反爬虫阻挠爬取,从页面控制台获取User-Agent赋给header,来标明访问者的信息。
定义变量sum用来计算中国区所有专卖店的数量,定义变量city_sum对每座城市中的专卖店数量进行统计。
创建一个名为“中国零售店”的csv文件,用来存放数据信息,并用f = open("中国零售店.csv", mode="a")表示对其中数据的append操作。
(2)查找到所需信息并进行输出
利用find函数调用,找到包含所有城市零售店信息的元素,即
在找到state后,为了计算每座城市中的零售店数量,在进入该座城市零售店的同时,将city_sum = 0,从而达到重新计算城市零售店数量的作用。再对lst_state中的每个state,通过find_all调用,按照先前的类似操作,依次查找class的值为stores-list-container、typography-body-reduced stores-list stores-list-zh_CN、store-address、address-lines的div元素。
在找到
(3)查找到对应专卖店图片并保存至本地
在分析零售店图片的链接时,发现这些链接的差异只有span元素中的a["data-store-number"]。所以,在找到span的基础上,进一步找到span中的a,并拼接每一个零售店图片的网址链接后赋给url_graph,即url_graph = "https://rtlimages.apple.com/cmc/dieter/store/16_9/"+a["data-store-number"]+".png?resize=375:211&output-format=jpg&output-quality=85&interpolation=progressive-bicubic"
查找好网址链接之后,由于零售店图片是动态加载图片,因此使用selenium浏览器渲染技术,爬取图片信息。在安装好Chorme与ChromeDriver的前提下,通过browser = webdriver.Chrome()来表明使用的浏览器为Chorme,随后通过browser.get(url_graph)来打开图片所在网页。为了防止延迟,所以time.sleep(3)来延后三秒。
在页面源代码中找到我们需要的img元素后,右键复制xpath,并通过pic = browser.find_element_by_xpath('/html/body/img')来将图片路径赋给pic,通过action = ActionChains(browser).move_to_element(pic) 来模拟移动鼠标到图片上,通过action.context_click(pic) 模拟右键图片,随后action.perform()执行,并通过pyautogui.typewrite(['v']) 模拟键盘按下v,从而选择保存图片,最后通过pyautogui.typewrite(['enter'])模拟键盘按下回车,完成保存图片的操作,并通过browser.close()关闭网页。
(4)数据可视化
利用echarts进行数据可视化,将各个城市专卖店的数量呈现于图标中。
这次的实验虽然过程艰难,但是正是因为在这样的情况中摸索,我收益丰富。先前对于python有过一定的学习了解,但并没有实际的运用,而这次实验正是给了我一个将知识落于实践中的机会。
虽然老师发了教程,不过由于是第一次实战,还是会有很多不理解的地方。教程中爬取的是电影信息网页,与我们爬取的专卖店信息的源代码有很大不同,所以一开始照葫芦画瓢的行为让我吃了很大的亏。后来发现照葫芦画瓢是无法完成作业的,于是开始理解代码的意义,这才明白其实是一层一层找到我们需要的元素,也就是span和address。
在理解代码之后,就比较好操作了。我在源代码中找到了囊括所有专卖店的元素,随后从这一个元素开始查找,一点一点往后找到了span和address,并进行f.write输出。
看到爬取的数据成功存到了csv中时,激动的心情是无法言喻的,却没想到实际上,我距离完成作业的日子还遥遥无期。我发现图片是在另一个网页中的,于是我试着找到了零售店图片网页的规律,从而成功将零售店图片的网页存到url_graph,本想直接write出图片,却发现只有空荡荡的文件夹。在询问有编程经验的朋友之后,试着打印查找到的img,却发现打印出的是[ ],这才意识到了问题。为了确认是否无法爬取出img,我又打印了整个页面的源代码,一个个查,发现确实是无法爬取。
这个问题困扰我许久,直到寻问同学,才知道这是动态加载图片,需要用selenium才能爬取,于是开始查找如何如何使用。在查找解决方法的过程中,我也了解到许多查询到的信息其实并不符合我们的需求,不该见到一个“教程”就原模原样地套用。
(1)各种由于没有pip install xxx而发生的错误,例如pip install BeautifulSoup4、pip install pyautogui,自己捣鼓了很久,以为是代码出现了问题,最后在百度上查询报错语句,才明白只是要pip stall。
(2)使用了太多次f.write以及在f.write中使用了逗号而报错,在查询了百度之后才意识到f.write的正确用法。
(3)对变量进行定义时,使用了int,结果报错,查询了之后才意识到python和c++对于变量是不同的,python不需要int。
(4)对于图片的爬取,绝对是整个实验中最难的一步。一开始我通过a[“href”]来查找图片所在页面,后来因为使用p.write爬取后的文件夹为空,问了认识的计算机行业的哥哥才发现text中就没有img这个元素,于是决定直接找到图片链接,并找到每个图片链接的规律,重新编写图片链接。之后问了同学,才知道因为是动态加载图片,所以无法爬取img,要使用selenium进行动态加载图片爬取,而selenium就是模拟鼠标与键盘操作,进行自动式保存。
在找到爬取的图片链接后,又发现re.findall无法找到img。不过因为发现实际上进行保存操作并不需要这一步,所以删掉后进行了相关调整,成功将图片依次保存至本地。
(5)browser = webdriver.Chrome()报错之后,查询了才知道是因为没有下载Chorme以及ChormeDriver,在网上找了教程下载之后就不再报错了。
import requests
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time
import pyautogui
int sum = 0;
city_sum = 0;
# 通过requests请求到网页
url = "https://www.apple.com.cn/retail/storelist/"
text = requests.get(url).text
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38'}
# 使用BeautifulSoup进行解析,后面的这个是html的解析器
main_page = BeautifulSoup(text, "html.parser")
# find找的是一个标签
# find_all找一堆标签
f = open("中国零售店.csv", mode="a") # r=read;w=write;a=append
CN = main_page.find("div", attrs={"class": "row align-top"}) # 找到最大涵盖
regions = CN.find_all("div", attrs={"class": "large-4 medium-12 state-column"}) # 分区
for region in regions:
lst_state = region.find_all("div", attrs={"class": "state"})
if len(lst_state) != 0: # 除去无数据行
for state in lst_state:
city_sum = 0;
lst_container = state.find_all("div", attrs={"class": "stores-list-container"})
if len(lst_container) != 0: # 除去无数据行
for container in lst_container:
lst_city = container.find_all("div", attrs={
"class": "typography-body-reduced stores-list stores-list-zh_CN"})
if len(lst_city) != 0: # 除去无数据行
for city in lst_city:
city_store = city.find_all("div", attrs={"class": "store-address"}) # 所有的store-address
if len(city_store) != 0: # 除去无数据行
for store in city_store: # 找到该城市里的每个store-address
lst_line = store.find_all("div", attrs={"class": "address-lines"})
if len(lst_line) != 0: # 除去无数据行
for line in lst_line: # 找到每一个零售店地点address-lines
sum=sum+1;
city_sum=city_sum+1;
lst_span = line.find_all("span")
if len(lst_span) != 0: # 除去无数据行
for span in lst_span: # 拿到每一个地点span
print(span.text) # 拿到地点span的文本信息
f.write(span.text) # strip() 默认去掉左右两端的空白(空格、换行符、制表符}
f.write("\n")
lst_address = line.find_all("address")
if len(lst_address) != 0: # 除去无数据行
for address in lst_address: # 拿到每一个地址address
print(address.text) # 拿到地址address的文本信息
f.write(address.text)
f.write("\n\n")
if len(lst_span) != 0: # 除去无数据行
for span in lst_span:
for a in span.find_all("a"):
url_graph = "https://rtlimages.apple.com/cmc/dieter/store/16_9/" + a["data-store-number"]+ ".png?resize=375:211&output-format=jpg&output-quality=85&interpolation=progressive-bicubic" # 拿到图片网页
browser = webdriver.Chrome()
browser.get(url_graph)
text = browser.page_source # 获取页面信息
browser.get(url_graph)
time.sleep(3)
pic = browser.find_element_by_xpath('/html/body/img')
action = ActionChains(browser).move_to_element(pic) # 移动到该元素
action.context_click(pic) # 右键点击该元素
action.perform() # 执行
pyautogui.typewrite(['v']) # 敲击V进行保存
# 单击图片另存之后等1s敲回车
time.sleep(1)
pyautogui.typewrite(['enter'])
time.sleep(5)
browser.close()
print("!!该城市共有", city_sum, "家专卖店")
f.write("\n\n!!该城市共有" + str(city_sum) + "家专卖店\n\n------------------------------------------------\n")
f.write("------------------------------------------------\n\n\n公司中国区专卖店的数量为:" + str(sum))
print("公司中国区专卖店的数量为:", sum)