数据采集第三次实验

作业①:


    我的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进行编写,这里主要是做输出处理和存入sqlite数据库,这里创建数据库表

                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")
        

        或许可以将数据插入MySQL

            # def open_spider(self, spider):
            #     print("opened")
            #     try:
            #         self.con = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="chen961122", db="test",
            #                                    charset="utf8")
            #         self.cursor = self.con.cursor(pymysql.cursors.DictCursor)
            #         self.cursor.execute("delete from movies")
            #         self.opened = True
            #     except Exception as err:
            #         print(err)
            #         self.opened = False
            #
            # def close_spider(self, spider):
            #     if self.opened:
            #         self.con.commit()
            #         self.con.close()
            #         self.opened = False
            #         print("closed")
        

        插入数据

        # sqlite
        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"]))
        # mysql
        self.cursor.execute("insert into movies (Mrank,Mtitle,Mactor,Mstar,Mcomment,Mscore,Mfile) values (%s,%s,%s,%s,%s,%s,%s)",
                                        (item["rank"], item["title"],item["actor"],item["star"], item["comment"], item["score"], item["file"]))
        

        这里第一次插入数据到数据库时,惊讶的发现数据少了几条,最后发现原来设定Mstar varchar(256)
        爬取到的部分数据太长导致无法插入,最后设定为Mstar text,解决了该问题。

        运行结果

        可以看见演员名单以及全部爬取下来了。

        心得体会

        在完成这项作业的过程中面临了多次网站异常,无法进入的问题,可能是爬取次数太多,

        以后一定先缓存页面的html,但是这题缓存的话link内的大量网页都要存,不太现实,(这里在线提问,如何防止被封)在进行多次测试,在这次爬虫第一次接触了较为复杂的逻辑再次进入一个页面爬,感觉以后学了selenium,对于这类问题的处理应该没有这么麻烦。其次就是对scrapy越来越熟练,感觉爬虫还是一项需要经常练习的技术,这次也使用了mysql和sqlite两种数据库对数据进行存储,感觉大同小异,最后感谢吴玲老师在实践课的指导,不然卡在某些debug部分就出不来了。。。

posted @ 2021-10-30 11:07  Aplical  阅读(26)  评论(0编辑  收藏  举报