数据采集与融合技术实践作业1

发布时间 2023-09-21 16:07:24作者: 102102137陈浩伟

作业1

爬取前37条大学排名实验

  • 首先点击链接进入网页,分析网页存储有排名、学校名称、省市、学校类型和总分的元素。分析得使用该网页使用自定义属性data-v-4645600d=""的tr元素进行作为包装。
  • 观察该html文档树,发现tr元素下共有6个td子节点,其中,第一个td子节点中的div元素存放排名数据,第二个td子节点下的class="name-cn"的a元素中存放学校名称,第三、四、五节点分别存放省市、学校类型和总分。
  • 根据分析结果编写findElement函数,用于寻找网页中所需的信息,尽可能实现精确定位,主要代码如下:
def findElement(html):
    soup = BeautifulSoup(html, 'html.parser')
    rows=[]
    try:
        #尽量精确查找
        tr_elements=soup.select("table tbody[data-v-4645600d] tr[data-v-4645600d]")
        for tr_element in tr_elements:
            td_elements = tr_element.select("td[data-v-4645600d]")
            No = td_elements[0].select_one("div.ranking").text.strip()
            UnName=td_elements[1].select_one("a.name-cn").text.strip()
            Province=td_elements[2].text.strip()
            Type=td_elements[3].text.strip()
            Score=td_elements[4].text.strip()
            row=No+'-'+UnName+'-'+Province+'-'+Type+'-'+Score
            rows.append(row)
    except Exception as err:
        print(err)

    return rows
  • 然后将读取到的数据存储到数据库中并提交保存
for row in rows:
    row=row.split('-')
    if(len(row)==5):
        #实现插入数据
        sql='insert into rankings (No,UnName,Province,Type,Score) values(?,?,?,?,?)'
        try:
            No=row[0]
            UnName=row[1]
            Province=row[2]
            Type=row[3]
            Score=row[4]
            cursor.execute(sql,(No,UnName,Province,Type,Score))
        except Exception as err:
            print(err)
# 数据库提交保存
con.commit()
con.close()
  • 但发现由于该网页一页仅显示30条数据,数据库中也仅存储了30条数据,根据要求爬取37条数据,因此需要实现翻页功能。使用selenium模拟点击翻页按钮进行爬取,代码如下:
url='http://www.shanghairanking.cn/rankings/bcur/2020'
    # 创建浏览器实例
    driver = webdriver.Edge()
    driver.get(url) #打开网页
    button_xpath = '//li[@title="下一页"]'
    button = driver.find_element(By.XPATH, button_xpath)
    button.click()
    # 获取新页面的 HTML 内容
    new_html = driver.page_source
    # 解析新页面的数据
    new_rows = findElement(new_html)
  • 即可成功输出获取前37条的大学排名数据(长度寿险仅展示部分)

心得体会

  1. 爬取网页的时候一定要仔细分析页面html结构,如果元素选择器写错就无法实现对应功能
  2. 实现翻页功能的时候使用selenium,创建webdriver进行模拟用户点击翻页按钮
  3. 做实验的时候不能心急,要细心仔细防止代码出bug

作业2

爬取商城数据实验

  • 准备爬取京东商城前60条书包的名称、价格、店铺数据,首先分析网页html结构,发现相关数据是被存放在class=”gl-warp clearfix”的ul中,其中有许多li元素,每个li元素中有一个商品的许多信息。分析所需信息,发现商品名称是被存放在class="p-name p-name-type-2"的div元素下target=”_blank”的a元素下的em元素中,价格被存放在class=”p-price”的div下的i元素中,店铺名被存放在class=”p-shop”的div元素下class="J_im_icon"的span元素中的target="_blank"的a元素中。分析完毕,编写程序核心代码如下:
#尽量精确查找
li_elements=soup.select('div[id="J_goodsList"] ul li')
for li_element in li_elements:
    Name=li_element.select_one('div[class="p-name p-name-type-2"] a[target="_blank"] em')
    if Name:
        Name=Name.text.strip()
        Name=Name.replace('\n', '')
    Price=li_element.select_one("div[class='p-price'] i")
    if Price:
        Price=Price.text.strip()
    Shop=li_element.select_one("div[class='p-shop'] span[class='J_im_icon'] a[target='_blank']")
    if Shop:
        Shop=Shop.text.strip()
    if Name and Price and Shop:
        row = Name + '-' + Price + '-' + Shop
    rows.append(row)
  • 但在实际运行时发现一个问题,如果直接复制输入搜索词“书包”后的网址进行爬取,获取到的html并不是实际有书包的那个页面,分析是因为京东网页内容是通过ajax动态渲染的,因此难以直接爬取静态数据,为解决此问题,使用webdriver模拟人输入“书包”并点击搜索按钮,然后爬取跳转后的网页,实现代码如下:
# 找到输入框并输入内容
input_element = driver.find_element(By.ID, "key")  # 使用 id 属性定位输入框
input_element.send_keys(item)  # 输入要搜索的文本

#点击按钮
button = driver.find_element(By.CSS_SELECTOR, '.button[aria-label="搜索"]')
button.click()
#动态等待页面成功加载
wait = WebDriverWait(driver, 10)  # 最大等待时间为10秒
wait.until(EC.text_to_be_present_in_element((By.XPATH,
    "//div[@class='crumbs-nav-item']/strong"), item))
html = driver.page_source
  • 存储数据到数据库和展示数据库的代码与上面类似,在此就不再展示,运行代码后即可爬取书包的商品列表:

  • 由于在程序中增加了input输入框,所有可以根据输入的需求爬取任意商品

心得体会

  • 京东这类大型网站的反爬措施都会做的很好,合法爬取数据时要注意找到合适的方法绕过反爬措施,本人采用的方法是webdriver驱动后使用手机进行扫码登录,这样的做法有些取巧,但很好用。

作业3

爬取并下载给定页面的图片实验

  • 创建findImg函数进行获取图像,由于是爬取页面中所有网页,因此无需分析类选择器,爬取页面所有的img元素并存储src即可,但需要分析图片存储的src,经过分析,img元素存储的src并不完整,如果要下载图像,需要在前面补上'https://xcb.fzu.edu.cn',寻找图片元素代码如下:
def findImg(html):
soup = BeautifulSoup(html, 'html.parser')
global rows
try:
    img_elements=soup.select('img')
    for img_element in img_elements:
        #确保src存在
        if img_element is not None and 'src' in img_element.attrs:
            imgurl='https://xcb.fzu.edu.cn'+img_element['src']
            rows.append(imgurl)
except Exception as err:
    print(err)
return rows
  • 存储所有元素的src后,调用自行创建的downloadImg函数把图片下载到本地
def downloadImg():
global rows
try:
    folder_path = 'downloadImages'
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)
    # 本地保存文件
    for index, img_url in enumerate(rows):
        # 生成文件名
        filename = f"image_{index}.jpg"
        # 拼接完整的文件路径
        file_path = os.path.join(folder_path, filename)
        urllib.request.urlretrieve(img_url, file_path)
except Exception as err:
    print(err)
  • 最后执行findImg和downloadImg函数,即可成功下载指定网页中的所有图像,结果如下:

心得体会

  1. 在爬取图片时,一定要注意img元素的src属性,观察它的src是否完整,如果不完整要自己补充url链接才能正确下载。