当前位置 博文首页 > 文章内容

    网络爬虫第一次作业——结合三次小作业

    作者: 栏目:未分类 时间:2020-09-27 18:02:15

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    作业①:

    1)UniversityRanking实验

      要求:爬取给定网址http://www.shanghairanking.cn/rankings/bcur/2020 的数据,屏幕打印爬取的大学排名信息。

    程序主要思路:

    graph TD A(获取给定网页字符内容) -->B(解析网页字符内容,提取需要的信息) B --> C(将提取到的信息设置好格式在控制台输出)

    代码:

    import urllib.request
    from bs4 import BeautifulSoup
    
    
    class SpiderUniversity(object):
        '''创建一个爬取给定网址大学排名信息的类'''
    
        def __init__(self,url):
            '''共同属性是网址url'''
            self.url = url
    
        def get_html(self):
            '''获取网址字符内容'''
            header= {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362"
            }
            req = urllib.request.Request(self.url, headers=header)
            html = urllib.request.urlopen(req).read()
            return html.decode()
    
        def parse_html(self):
            '''解析网址字符内容,提取需要的信息'''
            html = self.get_html()
            soup = BeautifulSoup(html, "html.parser")
            tags = soup.find_all("td")  #该网址所需信息都包含在td标签内
            return tags
    
        def output(colnames,tags):
            '''将提取到的信息设置好格式在控制台输出'''
            colnames = ['排名','学校名称','省市','学校类型','总分','办学层次']  #td标签包含的所有属性
            sep = "  "  #控制格式的分隔符
            seps = [14*sep,12*sep,14*sep,12*sep]
            colnum = len(colnames)
            datanum = len(tags)
            for i in range(colnum-1):
                try:
                    print(colnames[i],end=seps[i])
                except:
                    print(colnames[i])
            for i in range(0,datanum,colnum):
                for j in range(i,colnum+i-1):
                    string = tags[j].text.strip()
                    print(string, end=(16-len(string))*sep)
                print()
    
    
    if __name__ == "__main__":
        url = "http://www.shanghairanking.cn/rankings/bcur/2020"
        spider = SpiderUniversity(url)
        tags = spider.parse_html()
        spider.output(tags)
    

    运行结果部分截图:

      没利用中文空格chr(12288)尝试对齐真实太难了。原因是这样:在print中,空格是ASCII码为20的space,也就是半角空格。它的长度等于每个字母或数字的宽度,但远比汉字的宽度小,所以导致输出后的字符串长度参差不齐。

    将填充符修改为中文空格后(输出代码即output方法修改如下)

        def output(colnames,tags):
            '''将提取到的信息设置好格式在控制台输出'''
            colnames = ['排名','学校名称','省市','学校类型','总分','办学层次']  #td标签包含的所有属性
            sep = chr(12288) #控制格式的分隔符
            seps = [16*sep,14*sep,15*sep,15*sep]
            colnum = len(colnames)
            datanum = len(tags)
            for i in range(colnum-1):
                try:
                    print(colnames[i],end=seps[i])
                except:
                    print(colnames[i])
            for i in range(0,datanum,colnum):
                for j in range(i,colnum+i-1):
                    string = tags[j].text.strip()
                    print(string, end=(18-len(string))*sep)
                print()
    

    输出效果:

    2)心得体会:

      html都是以标签对的形式存储信息,因此找到信息所处位置的标签特征很关键。这里,我抓住了所需信息都存储在<td>...</td>标签对中的特征。
    主要收获如下:
    1、学习了编写爬虫程序的一般思路
    2、初步掌握了html标签对的结构形式
    3、熟悉了urllib.request以及bs4.BeautifulSoup的使用,学会如何用request获取网页内容并解码和利用BeautifulSoup遍历标签树进行解析

    作业②:

    1)GoodsPrices实验

      要求:自己选择某个商城商品比价定向爬虫,爬取该商城,以关键词“书包”搜索页面的数据,爬取商品名称和价格。(这里选择京东商城)

    程序主要思路:

    graph TD A(获取给定搜索关键词的url) -->B(获取给定页数的url) B --> C(利用正则表达式提取相应url下的商品名称和价格信息) C --> D(在第1页到第n页循环信息提取过程) D --> E(将提取到的所有信息格式化输出)

    代码:

    import requests
    import re
    
    
    class SpiderGoods(object):
        '''创建爬取京东搜索页面中的商品名和价格的类'''
    
        def __init__(self,url,pagenums=1):
            '''共同属性有网址url以及要搜索的总页数的pagenums'''
            self.url = url
            self.pagenums = pagenums
    
        def get_html_text(self,url):
            '''获取网址字符内容,此处要获取页面的完整商品信息还要利用selenium模仿滚动条下拉'''
            head = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362"
            }
            req = requests.get(url, headers=head,timeout=30)
            req.raise_for_status()
            # req.encoding = req.apparent_encoding  # 分析编码比较耗时,可手动设置
            req.encoding = 'utf-8'
            return req.text
    
        def search_keyword(self,name): #https://search.jd.com/Search?keyword=book&page=4
            '''返回京东指定关键词url'''
            # name_code = urllib.request.quote(name) #对汉字进行编码,这里支持中文所以不需要
            self.url += "?keyword=" + name  #京东搜索接口
            return  self.url
    
        def turn_page(self,pagenum):
            '''返回京东对应关键词搜索的制定页码url'''
            page_num = 2*pagenum - 1;  #理论页数与真实页数的映射关系
            return self.url + "&page=" + str(page_num)
    
        def name_filter(self,string):
            '''利用正则表达式过滤出商品名称'''
            reg = r'<li.+?data-sku.+?>'  #商品正文开始标志
            m = re.search(reg,string)
            start = m.end()
            string = string[start:]  #从商品正文开始过滤
            pattern = re.compile(r'(<em>[^¥][\s\S]*?</em>)')
            string = ','.join(pattern.findall(string))  #将列表类型转化为字符串以便后续处理
            reg = r"(<span .*?>.*?</span>)|<[\s\S]+?>|(\s)"
            names =  re.sub(reg,'',string).split(",")
            return names
    
        def price_filter(self,string):
            '''利用正则表达式过滤出商品价格'''
            reg = r"(<span .*?>[\s\S]*?</span>)"
            string = re.sub(reg, '', string)
            reg = r'<i>(\d+\.?\d*)</i>'
            pattern = re.compile(reg)
            prices = pattern.findall(string)
            return prices
    
        def output(self,names,prices):
            '''格式化输出函数'''
            colnames = ['序号','价格','商品名']
            form = "{0:<3}{1:^20}{2:^30}"
            print(form.format(colnames[0],colnames[1],colnames[2]))
            for i in range(len(names)):
                print(form.format(str(i+1),prices[i],names[i]))
    
        def search_pages_info(self):
            '''给出指定url、搜索页数的所有商品信息'''
            for i in range(1,self.pagenums+1):  #从第一页遍历到给定的搜索页数pagenums
                info_html = self.get_html_text(self.turn_page(i))
                names = self.name_filter(str(info_html))
                prices = self.price_filter(str(info_html))
                print("{:-^100}".format(i))  #分页输出标记
                self.output(names,prices)
    
    
    if __name__ == "__main__":
        url = "https://search.jd.com/Search"
        pagenums = 5
        key = "书包"
    
        spider = SpiderGoods(url,pagenums)
        spider.search_keyword(key)
        spider.search_pages_info()  #该方法中调用了其他类方法
    
    

    运行结果部分截图:

    2)心得体会:

      通过比对不同关键词搜索结果的url,以及同一关键词不同页数的url,可以发现该商城提供的搜索接口和翻页接口,比方说这里京东商城的关键词字段是keyword,页数字段是page,并且keyword支持中文(无需另外编码)以及页数n对应的page=2n-1。
      信息提取方面,通过观察我发现了商品名和价格所在的标签特征,由此体会到熟悉目标html结构是成功提取信息的重中之重。
    主要收获:
    1、学习了如何调用京东等网站的搜索接口以及翻页接口
    2、学习了应对一些类似网站的反爬技巧例如加header以及cookie。
    3、练习了如何使用正则表达式提取所需信息

    作业③

      要求:爬取一个给定网页http://xcb.fzu.edu.cn/html/2019ztjy或者自选网页的所有JPG格式文件(这里选择福州大学党委宣传部网页http://xcb.fzu.edu.cn

      程序主要思路:

    graph TD A(获取给定网页字符内容) -->B(从网址字符内容中提取出所有jpg格式图片的url路径,若为相对路径则要将其拼接为绝对路径) B --> C(下载图片到本地某个文件夹)

    代码:

    import requests
    import re
    import os
    
    
    class SpiderJpg(object):
        '''创建爬取JPG格式图片的爬虫类'''
    
        def __init__(self,url,header):
            '''共同属性有网址url以及UA字段'''
            self.url = url
            self.header = header
    
        def get_html_text(self):
            '''获取网址字符内容'''
            req = requests.get(self.url, headers=self.header, timeout=30)
            req.raise_for_status()
            # req.encoding = req.apparent_encoding  # 分析编码比较耗时,可手动设置
            req.encoding = 'utf-8'
            return req.text
    
        def extract_jpg(self,html_text):
            '''从网页所有字符内容中解析出与jpg图片有关的字符内容'''
            reg = r'<li>.+href="#".+jpg.+</li>'
            html_text = re.sub(reg,'',html_text)  # 去掉注释字段中的重复图片
            reg = r'"(/.+\.jpg)"|src="(.+\.jpg)'
            pattern = re.compile(reg,re.I)  # re.I忽略大小写
            return pattern.findall(html_text)
    
        def download_jpg(self,imgs):
            '''利用解析出的与jpg图片有关的字符内容将图片保存到本地'''
            current_path = os.getcwd()  # 获取当前路径,作为控制台输出提示
            print('There are {} pictures in total'.format(len(imgs)))
            for img in imgs:
                img_url = url + max(img) if max(img)[0] == '/' else max(img)  # 如果是相对路径则要拼接url成绝对路径否则不需要
                img_name = img_url.split("/")[-1]  # 文件名选取
                print('{}:{}'.format(img_name,img_url))
                img_con = requests.get(img_url, headers=self.header).content
                with open(img_name, 'wb') as f:  # 以二进制方式写入文件
                    f.write(img_con)
                    print('{} Downloaded in {}'.format(img_name, current_path))
    
    
    if __name__ == '__main__':
        url = "http://xcb.fzu.edu.cn"
        header = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362"
        }
    
        spider = SpiderJpg(url,header)
    
        html_text = spider.get_html_text()
        imgs = spider.extract_jpg(html_text)
        spider.download_jpg(imgs)
    

    运行结果部分截图:

    实际上程序文件(.py)会与图片在同一文件夹,这里为方便展示,将文件夹中的py文件暂时移出了(程序中使用默认的相对路径)

    2)心得体会:

    观察目标网页的html结构,可以发现提取所有jpg格式图片有几个难点:
    (1)后缀名有大小写,jpg和JPG
    (2)目标jpg路径同时存在绝对路径和相对路径
    (3)html文本中注释字段有重复的图片,例如http://xcb.fzu.edu.cn/themes/10995/portal188_11187_10379296/images/topic1.jpghttp://xcb.fzu.edu.cn/themes/10995/portal188_11187_10379296/images/cast1.jpg
    我通过对整体html结构以及图片链接所在位置html结构的分析,过滤重复图片,完善爬取规则,最后终于做到了对该网页jpg格式图片不重不漏的爬取
    主要收获:
    1、加强了正则表达式在设计爬取规则中的应用
    2、学会如何通过爬取到的url链接将图片文件保存到本地