1. 程式人生 > 其它 >資料採集第三次實驗

資料採集第三次實驗

作業①:


    我的Gitee(單)
    我的Gitee(多)
  • 要求:指定一個網站,爬取這個網站中的所有的所有圖片,例如中國氣象網

    http://www.weather.com.cn

    。分別使用單執行緒和多執行緒的方式爬取。(限定爬取圖片數量為學號後4位)

  • 輸出資訊:

    將下載的Url資訊在控制檯輸出,並將下載的圖片儲存在weather子檔案中,並給出截圖。

    (1)爬取中國氣象網網頁內容

    單執行緒實驗過程
    1.先通過主url,檢查頁面,獲取到該頁面下存在的所有a[href]連結,全部存下,後續使用
    獲取類似的link,確保圖片數量。

    (程式碼如下)

    start_url = "http://www.weather.com.cn"
    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"}
    r = requests.get(start_url, headers=header)
    r.raise_for_status()
    r.encoding = r.apparent_encoding
    data = r.text
    soup = BeautifulSoup(data, "html.parser")
    # 解析html
    a = '<a href="(.*?)" '
    linklist = re.findall(re.compile(a), str(soup))
    # 正則表示式獲取link
    

    2.定義封裝一個獲取各個url下圖片連結的函式,得到以下程式碼:

    def imagelist(link):
        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"}
        r = requests.get(link, headers=header)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        data = r.text
        soup = BeautifulSoup(data, "html.parser")
        images = soup.select("img")
        p = r'img.*?src="(.*?)"'
        imagelist = []
        str1 = re.findall(p, str(images), re.S)
        # # print(str1)
        for i in range(len(str1)):
            if str1[i] not in imagelist:
                imagelist.append(str1[i])if str1[i] else ""
                # print(imagelist)
                # print(len(imagelist))
        return imagelist
    

    3.定義一個下載圖片的函式(如下)

    def download(link):
        global count
        file = "E:/weather/" + "第" + str(count+1) + "張.jpg"  # file指先在指定資料夾裡建立相關的資料夾才能爬取成功
        print("第" + str(count+1) + "張爬取成功")
        count += 1
        urllib.request.urlretrieve(url=link, filename=file)
    

    4.執行結果:

    多執行緒實驗過程
    1.與單執行緒實驗類似,運用多執行緒加快載入速度。
    首先解析html,同樣的獲取多個link

    start_url = "http://www.weather.com.cn"
    headers= {
        "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"}
    r = requests.get(start_url, headers=headers)
    r.raise_for_status()
    r.encoding = r.apparent_encoding
    data = r.text
    soup = BeautifulSoup(data, "html.parser")
    linklist = []
    a = '<a href="(.*?)" '
    linklist1 = re.findall(re.compile(a), str(soup))
    for i in range(len(linklist1)):
        if i < 20:
            linklist.append(linklist1[i]) if linklist1[i] != "javascript:void(0);" else ""
    # print(len(linklist))
    # 到此處便獲取了所有的link
    threads=[]
    for i in linklist:
        imageSpider(i)
    # 將link呼叫回imageSpider函式
    

    2.獲取單個頁面內的所有img連結

    soup=BeautifulSoup(data,"html.parser")
            images=soup.select("img")
            for image in images:
                try:
                    src=image["src"]
                    url=urllib.request.urljoin(start_url,src)
    

    這裡獲取到的圖片連結是img下的src,但是由於網站原因當中混入了http://www.weather.com.cn此網站,因此這裡做下判斷:

                    if url not in urls:
                        if not str(url).endswith('.cn'):
    

    3.最後封裝download函式,用於多執行緒爬取:這裡先對每個圖片url進行處理,獲取每個圖片連結的後四位,即圖片的格式:最後寫入檔案

    def download(url,count):
        try:
            if(url[len(url)-4]=="."):
                type=url[len(url)-4:]
            else:
                type=""
            req=urllib.request.Request(url,headers=headers)
            data=urllib.request.urlopen(req,timeout=100)
            data=data.read()
            fobj=open("E:\images\\"+str(count)+type,"wb")
            fobj.write(data)
            fobj.close()
            print("downloaded "+str(count)+type+"\n")
        except Exception as err:
            print(err)
    

    4.多執行緒設定:

    T=threading.Thread(target=download,args=(url,count))
                            T.setDaemon(False)
                            T.start()
                            threads.append(T)
    
    for t in threads:
        t.join()
    print("The End")
    

    5.執行結果:

    心得體會:

    通過本次實驗首先複習了從網站上下載圖片的基本操作,以及如何使用多執行緒來加快下載速度。本次實驗不同的地方在於不在同一個單一頁面內爬取圖片,先要通過主url,找到他的所有分支,在對限定數量的圖片進行下載。總體完成情況較好,也較熟練地掌握了。

    作業②:


      我的Gitee
    • 要求:使用scrapy框架復現作業①。
    • 輸出資訊: 同作業①

      (2)利用scrapy框架下載網站圖片

      實驗過程
      1.首先建立scrapy專案

      2.編寫myspiders,建立myspiders類,定義爬蟲名稱以及start_urls

      class myspides(scrapy.Spider):
          # 設定基礎引數
      
          name = 'myspiders'
          start_urls = ["http://www.weather.com.cn"]
      

      2.同作業1,定義一個獲取圖片連結的函式

          def image(self,link):
              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"}
              r = requests.get(link, headers=header)
              r.raise_for_status()
              r.encoding = r.apparent_encoding
              data = r.text
              soup = bs4.BeautifulSoup(data, "html.parser")
              images = soup.select("img")
              p = r'.+?src="(\S+)"'
              imagelist = []
              str1 = re.findall(p, str(images), re.S)
              # # print(str1)
              for i in range(len(str1)):
                  if str1[i] not in imagelist:
                      imagelist.append(str1[i]) if str1[i] else ""
                      # print(imagelist)
                      # print(len(imagelist))
              return imagelist
      

      3.在parse函式中同樣的獲取a[href]

      a = soup.select("a[href]")
                      links = []
                      for link in a:
                          links.append(link["href"]) if link["href"] != 'javascript:void(0)' else ""
      

      4.之後便進行圖片的下載:

      for i in images:
          item["images"]= i
          item["name"] = "E:/weather/" + str(count+1) + ".jpg"
          urllib.request.urlretrieve(item["images"], filename=item["name"])
          count += 1
          if count <=112:
             break
          yield item
      

      5.在item.py中,定義myspiders中運用到的item項,這裡定義item["images"],item["name"]

      6.在setting.py檔案中進行設定:
      (注意這裡要忽略網站爬蟲協議)

      ROBOTSTXT_OBEY = False
      

      在對其他設定:

      執行結果

      心得體會

      該實驗的主要爬蟲思路,和作業①一致,主要是要求用scrapy框架進行實驗,估計老師是為了讓我們熟練使用scrapy框架,經過這次實驗也確實很熟練了,詳細原因見實驗三。

      作業③:


        我的Gitee
      • 要求:爬取豆瓣電影資料使用scrapy和xpath,並將內容儲存到資料庫,同時將圖片儲存在imgs路徑下。

        候選網站: https://movie.douban.com/top250

      • 輸出資訊:
        序號 電影名稱 導演 演員 簡介 電影評分 電影封面
        1 肖申克的救贖 弗蘭克·德拉邦特 蒂姆·羅賓 希望讓人自由 9.7 ./imgs/xsk.jpg
        2....

        (3)爬取豆瓣相關資訊

        實驗過程
        1.先對豆瓣網頁進行觀察,可以發現需要爬取的資訊都分佈在頁面之中:

        但是這裡發現了一個問題對於要爬取的演員名單,在該頁面下並不完整,導演名稱較為混亂,為了完善該爬蟲,這裡決定在外部介面爬取該部分連結,進入該link,對演員資訊進行爬取。

        2.分析完網頁接著就用scrapy框架,對該網站進行爬取。首先建立scrapy專案。

        3.編寫主函式myspiders,建立myspides(scrapy.Spider):定義爬蟲名和start_urls

         name = 'myspiders'
         allowed_domains = ['movie.douban.com']
         start_urls = ['https://movie.douban.com/top250?start=0']
        
        

        4.封裝parse(self, response)函式,解析html,利用xpath對資料進行查詢

         title = li.xpath("./div[@class='pic']/a/img/@alt").extract_first()
         img = li.xpath("./div[@class='pic']/a/img/@src").extract_first()
         score = li.xpath("./div[@class='info']/div[@class='bd']/div[@class='star']/span[position()=2]/text()").extract_first()
         rank = li.xpath("./div[@class='pic']/em/text()").extract_first()
         comment = li.xpath("./div[@class='info']/div[@class='bd']/p[position()=2]/span/text()").extract_first()
        

        5.對圖片的下載處理:

        file = "E:/movie/" + str(item["title"]) +  ".jpg"  # file指先在指定資料夾裡建立相關的資料夾才能爬取成功
        urllib.request.urlretrieve(item["img"], filename=file)
        item["file"] = file.strip()
        

        6.繼續利用xpath爬取該電影欄目的連結,進入該連結爬取導演,演員名稱

        link = li.xpath("./div[@class='pic']/a/@href").extract_first()
        soup = bs4.BeautifulSoup(r.text, "html.parser")
        title = soup.select("div[id='info']")
        reg = r'<a href=".*?" rel=".*?">(.*?)</a>'
        actor = re.findall(reg,str(title))
        # print(actor[0])
        act = soup.select("span[class='actor'] span[class='attrs']")[0].text
        # print(act)
        item["actor"] = actor[0] if actor else ""
        item["star"] = act if act else ""
        yield item
        

        7.最後對myspiders.py進行翻頁處理:

        page = selector.xpath(
                            "//div[@class='paginator']/span[@class='thispage']/following-sibling::a[1]/@href").extract_first()
        print(page)
        link_nextpage = "https://movie.douban.com/top250" + str(page)
        if page:
           url = response.urljoin(link_nextpage)
           yield scrapy.Request(url=url, callback=self.parse, dont_filter=True)
        

        8.接著對item.py檔案進行處理,定義所需的items類

        class DemoItem(scrapy.Item):
            # define the fields for your item here like:
            # name = scrapy.Field()
            title = scrapy.Field()
            img = scrapy.Field()
            link = scrapy.Field()
            score = scrapy.Field()
            comment = scrapy.Field()
            rank = scrapy.Field()
            actor = scrapy.Field()
            star = scrapy.Field()
            file = scrapy.Field()
            pass
        

        9.對settings要進行處理,一開始我只設定了'demo.pipelines.DemoPipeline': 300,發現無論怎麼爬取都沒有內容,苦苦尋找原因最後發現因為沒有忽略網站爬蟲協議,所以做了如下修改:

        ROBOTSTXT_OBEY = False
        USER_AGENT = 'demo (+http://www.yourdomain.com)'
        

        10.對pipelines進行編寫,這裡主要是做輸出處理和存入資料庫,這裡建立資料庫表

                if flag:
                    self.con = sqlite3.connect("movie.db")
                    self.cursor = self.con.cursor()
                    try:
                        self.cursor.execute(
                            "create table movie (int Mrank ,Mtitle varchar(256),Mactor varchar(256),Mstar text,Mcomment varchar(256),Mscore varchar(256),Mfile varchar(256),constraint pk_movie primary key (Mrank,Mtitle))")
                        flag = False
                    except:
                        self.cursor.execute("delete from movie")
        

        插入資料

        self.cursor.execute("insert into movie (Mrank,Mtitle,Mactor,Mstar,Mcomment,Mscore,Mfile) values (?,?,?,?,?,?,?)",
                                    (item["rank"], item["title"],item["actor"],item["star"], item["comment"], item["score"], item["file"]))
        

        這裡第一次插入資料到資料庫時,驚訝的發現數據少了幾條,最後發現原來設定Mstar varchar(256)
        爬取到的部分資料太長導致無法插入,最後設定為Mstar text,解決了該問題。

        執行結果

        可以看見演員名單以及全部爬取下來了。

        心得體會

        在完成這項作業的過程中面臨了多次網站異常,無法進入的問題,可能是爬取次數太多,

        以後一定先快取頁面的html,在進行多次測試,在這次爬蟲第一次接觸了較為複雜的邏輯再次進入一個頁面爬,感覺以後學了selenium,對於這類問題的處理應該沒有這麼麻煩。其次就是對scrapy越來越熟練,感覺爬蟲還是一項需要經常練習的技術,這次也使用了mysql和sqlite兩種資料庫對資料進行儲存,感覺大同小異,最後感謝吳玲老師在實踐課的指導,不然卡在某些debug部分就出不來了。。。