关于爬虫中几个常用库的使用方法总结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于爬虫中几个常用库的使用方法总结相关的知识,希望对你有一定的参考价值。

(关于爬虫中几个常用库的使用方法总结)

关于爬虫中几个常用库的使用方法总结

  • 学了半个多月的爬虫了,用这个案例总结一下各个爬虫库的用法。当然后面还有更重要和更好用的方法,再等后面学到,再做总结了。

1. 目标

  • 这个题目的要求很简单,用已学的方法,爬取某知名二手房源的信息。因为涉及到一些敏感的信息,只要求获取只个简单的字段即可。

1.1 爬取某知名网站的相关信息

  • 具体如下:
    • (1)爬取某网的房源信息,包括“标题,位置,户型,总价,单价”几个信息的收集并保存。
    • (2)网址是:https://cs.lianjia.com/ershoufang/

1.2 信息库及类的导入

  • 下面分几种方法解决这个答题,所以用到的库有如下几种:
    • requests、lxml的xpath对象方法、re正则表达式、BeautifuSoup对象方法、pyquery对象方法、parsel的Selector方法
  • 有时候,获取源代码和解析数据,是好几种方法一起来解决,在下面的几个解决方案中,都有提到。

2. 网页分析

  • 接下来,就是网页的分析。打开网页如下:
  • 再往下拖动,看到该页有30条信息,下面只看到局部的几条信息。其中每一条信息用蓝色方框圈起来,而红色方框圈起来的,是一条广告信息,是无用信息,后面在解析的时候给予剔除。

2.1 伪装问题

  • 根据政策法规的要求,我们不能随便用不正当手段爬取绝大多数网站的有用信息。不过动机单纯、行为正常,且没有恶意,出于研究目的,在控制好频率的情况下,爬取一些网站公开内容,是可以的。
  • 但是绝大多数网站都有反爬机制,它不管你的动机如何,一旦越限,它就会毫不客气的唯你是问,让你百口莫辩。
  • 因此,我们在这里,也了解一下这次这个网站的Robots协议,方法如下:
    • 打开https://cs.lianjia.com/robots.txt,得到如下robots内容:
      User-agent: Baiduspider
      Allow: *?utm_source=office*
      
      User-agent: *
      sitemap: https://cs.lianjia.com/sitemap/cs_index.xml
      Disallow: /rs
      Disallow: /huxingtu/p
      Disallow: /user
      Disallow: /login
      Disallow: /userinfo
      Disallow: *?project_name=*
      Disallow: *?id=*
      Disallow: *?house_codes=*
      Disallow: *?sug=*
      
    • 以上可以看到,我们今天所要爬取的目录ershoufang是被允许爬取的。
    • 再用如下方法:
    • 可以看到,这个网站的robots.txt文件是允许爬虫抓取ershoufang里面的数据的。
    • 基于以上原因,我们只加一个请求头,是可以了。方法前面写过:右击网页空白处“检查”——>“Network”——>Filter(筛选器)——>Doc(文档)——>刷新网页——>选择Name(名称)下的记录——>再找到右侧最底部,就找到User-Agent字符串头。复制出来 做成字典的模样,定义一个headers即可。

2.2 元素选取

  • 上图说明,如下:
  • 左侧小箭头就是选择器,使用的时候点它一下,然后去网页里面晃动几下,就看到一片区域对应一大段代码。这里,我们看到<ul>标签里,就对应本页所有的房源信息,就是<ur>下的<li>标签,仔细数一下,一共30条记录,对应30条房源,当然,其中第6条是广告,不过在爬虫库解析下,得到的数据是一一对应为30条的。
  • 每一条的<div>标签下,都包含我们想要的数据,截图如下:

2.3 分页面分析

  • 页面到最底部,有1到100页码,所以想抓取的信息可以有100页300条,本例只取前5页,用for循环实现翻页取信息,在下面代码实现。因为在翻页的过程中,地址栏里出现/pg2/、/pg3/的字样。所以在定义网址的时候,用如下方法:
    def __init__(self, num):
            self.url = https://cs.lianjia.com/ershoufang/pg/.format(num)
    
     # 爬取5页的数据
    for n in range(1, 6):
        print(正在爬取第页.format(n))
        spider = LianJian_changsha(n)
    

2.3 爬虫库的选用

  • 一般情况下,爬取数据不是太多的时候,用requests库结合一般的方法就能完成,大型数据获取并分析的情况,要用到更高级的方法。所以根据需求选择合适的爬虫库,也应该知道。因为是初学,对于下面的分析不一定准确,欢迎编程经验多的大佬们,批评指正。

2.3.1 保存文本的方法

  • 除了即时打印输出到控制台的办法外,保存爬虫数据最基础的自然是文本文件了。

  • 方法很简单,就是用操作文件和文件夹的办法,实现保存。涉及到路径的时候,要导入os库,然后有两种书写格式,如下 :

    # 第一种方法,后面需要手动close关闭文件对象
    filename = open(file1.txt,w)
    filename.close()
    

    # 第二种方法,后面不需要带close方法,也可以确保文件正常关闭
    with open(file2.txt,w) as f2:
    		f2.write(data)
    

2.3.2 保存二进制的方法

  • 上面提到,文本文件的获取过程,首先是获取源代码,再解析筛选得到文本内容,如果想要的数据变量为data的话,可以用text方法得到,如print(data.text)。而要得到图像或者音视频数据的话,就要先得到它们的二进制数据,用content方法,如print(data.content)。
  • 后面例子中,会经常遇到,爬虫学习的过程中,有你有我,我们一起作伴,慢慢走在路上,就会不知疲倦。

2.3.3 哪个更简单

  • 哪个爬虫的库用起来更简单,也要看获取到的网页格式,像本节提到的例子,用lxml里面的xpath方法比较简单。我说的简单,其实就是容易理解和使用,真正书写代码也可能很繁琐。我们在验证结果的时候,要不时的print一下,以此纠正解析得到的数据是多了还是少了。
  • 如果按照书写简洁来作为简单的话,目前学到的bs4和pyquery库就特别简单。很多时候,感觉比正则表达式的方法还要简单。

2.3.4 哪个更效率

  • 效率最高的爬虫方法,目前不敢定论,但是感觉requests以后,再用xpath方法是最麻烦的,遇到源代码中的多个ul列表、li列表、span列表等,还有各种空格乱码时,特快灵麻烦。在掌握了好的方法和熟练运用正则表达式之后,xpath能不用就不用,但是本节还是以这个为例讨论,毕竟这个是基础。
  • 我感觉,bs4和pyquery效率就蛮高,但是感觉目前掌握的方法中parsel方法更胜一筹。
  • 除了上面提到的,还有一个压箱底的东西,那就是CSS选择器,这个东西在几个重要的爬虫库里面经常用到,而且经常一步到位,实在是手术刀式的工具。

2.4 数据保存的问题

    • 爬虫得到的结果,才是我们想要的。有的不是太重要,可以略加展示或保存。有的结果很重要,这些数据就要用相当可靠的方式保存起来,以供后期的分析和使用。

2.4.1 打印输出

  • 这是最简单的展示方法,只是在控制台打印输出一下,看完就消失,对于不是重要的信息,完全够用了。

2.4.2 文本保存

  • 爬虫获取的数据,以最简单的文本格式保存,上面已经提到,后面复习爬虫库的使用时,还会再提到 ,在此不再赘述。

2.4.3 csv文件存储

  • 我用excel格式保存数据的时候,遇到了不能保存翻页信息的情况,当掌握了csv文件存储方法后,一下子感觉轻松解决了。因为csv文件完全可以用excel表格的形式打开,而且更简洁效率。

2.4.4 mysql存储

  • 这个数据库用来存储数据,是比较哪个和哪个的(敏感词&^%#@¥),不光是因为MySql在大型程序中应用,关键是很多爬虫得到的数据足够重要,用MySql保存也足够正式。这为后面的数据分析作了良好的支持。

2.4.5 MongoDB存储

  • 这个数据库是非关系型数据库,其内容存储的形式累死JSON对象。这次也学习一下这个数据库的保存方式,这之前要从头学习它的安装、初始化和建表、插入数据的方法。

3. 方法罗列

  下面的几种爬虫方法是目前我个人学到的,为了帮助记忆理解,在此加以记叙和总结。其中发现,各种方法很少有单独存在的情况,一是因为一个方法完成不了,另一个原因是,几种方法的组合使用,效率大大提高。

3.1 requests方法

  • 这个方法,是后面xpath方法、re正则表达式的前提条件。因为都用requests模块的get请求方法和post请求方法得到整个网页源代码之后,再进行xpath解析和正则匹配的。实际用法,在后面放上案例进行学习。

3.2 lxml的xpath方法

  • 按照上面的分析,用代码实现目标要求。全部代码如下:
    # -*- coding:utf-8 -*-
    """
        # @Time:2022/12/19 0019 13:53
        # @Author:晚秋拾叶
        # @File:链家长沙房源.py
        # @PyCharm之Python
        # 完成“标题,位置,户型,总价,单价”几个信息的收集并保存
    """
    import os
    import csv
    import requests
    from lxml import etree
    
    
    class LianJian_changsha(object):
        def __init__(self, num):
            self.url = https://cs.lianjia.com/ershoufang/pg/.format(num)
            self.headers = 
                User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54,
                
            
    
         1. 发起请求并获取数据 
    
        def get_data_index(self):
            # 可以使用urlli库里的方法from urllib.request import Request
            # 即用urllib中的request模块里的Request类定义解析的网页数据,
            # 不过需要用read()读取二进制内容,再用decode(utf-8)转为Unicode编码
            # 所以不如requests库来得方便
            """ (1)用到第一个库requests的get方法,获取总的网页源代码 """
            resp = requests.get(self.url, headers=self.headers)
            resp.encoding = utf-8
            if resp.status_code == 200:
                return resp.text
            else:
                return None
    
         2. 把得到的网页文本信息进行解析 
    
        def parse_data_index(self, resp):
            # 实例化一个etree的HTML类对象,这也是后面xpath解析的对象
            """ (2)用到第二个库lxml里的etree模块,构造一个xpath对象 """
    
            html_xpath = etree.HTML(resp)
    
            # 找到包含房源信息的总模块,可以看作为列表形式
            """ (3)xpath方式完成解析数据 """
            sellListContent = html_xpath.xpath(//ul[@class="sellListContent"]//li)
            list_context = []
            i = 0
            for data in sellListContent:
                title = data.xpath(.//div[@class="title"]/a/text())[0]
                position = ,.join(data.xpath(./div[1]/div[2]//a/text())).replace( , )
                houseInfo = data.xpath(.//div[@class="houseInfo"]/text())[0].replace(|, ,).replace( , )
                priceInfo = data.xpath(.//div[contains(@class,"totalPrice")]/span/text())[0] + "万元"
                unitPrice = data.xpath(.//div[@class="unitPrice"]/span/text())[0]
    
                dict_data = 标题: title, 位置: position, 户型: houseInfo, 总价: priceInfo, 单价: unitPrice
                i += 1
                print(f"现在是第i条数据")
                print(dict_data)
                list_context.append(dict_data)
            return list_context
    
        def write_data(self, resp):
            # 判断是否已经有这个csv文件,如果有,则不再添加标头,只追加记录
            if os.path.exists("LianJia_changsha.csv"):
                with open("LianJia_changsha.csv", "a", newline=, encoding=utf-8) as f:
                    fields = [标题, 位置, 户型, 总价, 单价]
                    writer = csv.DictWriter(f, fieldnames=fields)
                    for data in self.parse_data_index(resp):
                        writer.writerow(data)
            else:
                with open("LianJia_changsha.csv", "w", newline=, encoding=utf-8) as f:
                    fields = [标题, 位置, 户型, 总价, 单价]
                    writer = csv.DictWriter(f, fieldnames=fields)
                    writer.writeheader()
                    for data in self.parse_data_index(resp):
                        writer.writerow(data)
    
        """ 3. 把上面方法整合起来完成业务逻辑"""
    
        def run(self):
            resp = self.get_data_index()  # 获取网页信息
            self.write_data(resp)
    
    
    if __name__ == __main__:
        # 爬取5页的数据
        for n in range(1, 6):
            print(正在爬取第页.format(n))
            spider = LianJian_changsha(n)
            spider.run()
    
    
    • 掌握了合适的方法,加以处理,就得到了想要的数据,其中的带广告的那个li标签,也合理的规避了。上面这个方法用了csv格式保存

3.3 re正则表达式

  • 下面是用re正则表达式的方法完成问题解决。还是先用xpath缩小一下源代码的范围,做出<li>标签的列表来。在用re处理字符串的过程中,遇到了检查元素的代码和xpath获取源代码不一样的情况。如图:
  • 检查元素的代码:
  • 由此可见,想要正则匹配到我们需要的数据,只有得到真正的源代码才能够获取。下面用requests和xpath方法得到li标签列表。
  • 上面截图是爬取到的li标签列表,复制出一个li标签到http://www.wetools.com/html-formatter网站,稍微纠正下,再复制到记事本里面,更容易寻找对应的数据。
  • 下面把得到的源代码放到记事本中。
  • 再把包含一个数据的一大段代码复制到VSCode程序里,加以正则处理。

  • 注意想要的数据用(.*?)代替,然后方便用group(n)的方法得到。
  • 在保存为字符串形式的时候,需要一些转换字典为字符串的情况,代码中已经作了注释。
  • 最后,我把这个用记事本保存成txt文件,整个代码如下:
    # -*- coding:utf-8 -*-
    """
        # @Time:2022/12/19 0019 13:53
        # @Author:晚秋拾叶
        # @File:链家长沙房源.py
        # @PyCharm之Python
        # 完成“标题,位置,户型,总价,单价”几个信息的收集并保存
    """
    import os
    import re
    import json
    import requests
    from lxml import etree
    
    
    class LianJian_changsha(object):
        def __init__(self, num):
            self.url = https://cs.lianjia.com/ershoufang/pg/.format(num)
            self.headers = 
                User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54,
                
            
    
         1. 发起请求并获取数据 
    
        def get_data_index(self):
            # 可以使用urlli库里的方法from urllib.request import Request
            # 即用urllib中的request模块里的Request类定义解析的网页数据,
            # 不过需要用read()读取二进制内容,再用decode(utf-8)转为Unicode编码
            # 所以不如requests库来得方便
            """ (1)用到第一个库requests的get方法,获取总的网页源代码 """
            resp = requests.get(self.url, headers=self.headers)
            resp.encoding = utf-8
            if resp.status_code == 200:
                return resp.text
            else:
                return None
    
         2. 把得到的网页文本信息进行解析 
    
        def parse_data_index(self, resp):
            # 进一步筛选想要的源代码内容,缩小范围
            """ (2)还是用xpath定位好范围 """
            html_xpath = etree.HTML(resp)
            sellListContent = html_xpath.xpath(//ul[@class="sellListContent"]//li)
           
            try:
                """ (3)re正则方式完成解析数据 """
                for data in sellListContent: 
                    # 转换为字符串,以便后面进行正则获取  
                    htmlStr = etree.tostring(data,encoding="utf-8").decode()
                
                    title_pattern = re.compile(<div\\sclass="title">.*?data-sl="">(.*?)</a>, re.S)
                    position_pattern = re.compile(class="positionIcon".*?region">(.*?)</a>.*?target="_blank">(.*?)</a>, re.S)
                    houseInfo_pattern = re.compile(class="houseIcon".*?/>(.*?)</div>, re.S)
                    priceInfo_pattern = re.compile(class="totalPrice totalPrice2">.*?class="">(.*?)</span>.*?</i>, re.S)
                    unitPrice_pattern = re.compile(class="unitPrice.*?<span>(.*?)</span>, re.S)
                    title = re.search(title_pattern, htmlStr).group(1)
                    position = re.search(position_pattern, htmlStr)
                    positiongroup = position.group(1) + position.group(2)
                    houseInfo = re.search(houseInfo_pattern, htmlStr).group(1)
                    priceInfo = re.search(priceInfo_pattern, htmlStr).group(1) +"万元"
                    unitPrice = re.search(unitPrice_pattern, htmlStr).group(1)
                    # 注意,下面字典处理的时候,一定要用""而不能用
                    yield "标题": title, "位置": positiongroup, "户型": houseInfo, "总价": priceInfo, "单价": unitPrice    
            except IndexError:
                pass
        def write_data(self, content):
            with open(链家长沙房源信息.txt, a,encoding=utf-8) as f:
                # 因为获取的数据是字典类型,转换为字符串写入文本文件,而且还不能显示为ascii,否则看不到汉字
                f.write(json.dumps(content,ensure_ascii=False)+"\\n")
    
        """ 3. 把上面方法整合起来完成业务逻辑"""
        def run(self):
            resp = self.get_data_index()  # 获取网页信息
            for data in self.parse_data_index(resp):
                print(data)
                self.write_data(data)
    
    
    if __name__ == __main__:
        # 爬取5页的数据
        for n in range(1, 6):
            print(正在爬取第页.format(n))
            spider = LianJian_changsha(n)
            spider.run()
    
    
  • 完成后,效果如下:

3.4 BeautifuSoup方法

  • 再学习一下bs4的写法。
  • 这个库的功能很强大,方法有节点选择器,有方法选择器find_all,再加上强大的CSS选择器。用了更简短的代码,完成了既定目标。完整代码如下:
    # -*- coding:utf-8 -*-
    """
        # @Time:2022/12/19 0019 13:53
        # @Author:晚秋拾叶
        # @File:链家长沙房源bs4.py
        # @PyCharm之Python
        # 完成“标题,位置,户型,总价,单价”几个信息的收集并保存
        # 这次用excel方法保存
    """
    import requests
    import openpyxl
    from bs4 import BeautifulSoup
    
    
    class LianJian_changsha(object):
        def __init__(self):        
            self.headers = 
                User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54,
                   
    
    
         1. 发起请求并获取数据 
    
        def get_response(self,url):
            # 可以使用urlli库里的方法from urllib.request import Request
            # 即用urllib中的request模块里的Request类定义解析的网页数据,
            # 不过需要用read()读取二进制内容,再用decode(utf-8)转为Unicode编码
            # 所以不如requests库 来得方便
            """ (1)用到第一个库requests的get方法,获取总的网页源代码 """        
            resp = requests.get(url, headers=self.headers)
            resp.encoding = utf-8
            if resp.status_code == 200:
                return resp.text
            else:
                return None
    
         2. 把得到的网页文本信息进行解析 
        def parse_data(self):
            data_list = []
            for i in range(1,6):
                # 因为要分布保存到excel中,而且用的是openpyxl方法,所以把页面地址的更迭放在这里
                url = https://cs.lianjia.com/ershoufang/pg/.format(i)
                resp = self.get_response(url)
                # 进一步筛选想要的源代码内容,缩小范围
                """ (2)这个库的功能强大,用select方法和节点选择器,加上CSS选择器,代码简洁好用 """
                soup = BeautifulSoup(resp, lxml)
                sellListContent = soup.select("ul li .info.clear")
                i = 0
                # 解析并提取数据            
                for data_div in sellListContent:
                    # 下面的[0]用法,其实是列表索引,与xpath的位置选择不一样
                    title= data_div.select("a")[0].text
                    position = data_div.select("div .positionInfo")[0].text
                    houseInfo = data_div.select("div .houseInfo")[0].text
                    priceInfo = data_div.select(".totalPrice.totalPrice2 span")[0].text + "万元"
                    unitPrice = data_div.select(".unitPrice")[0].text  
                    data = [title,position,houseInfo,priceInfo,unitPrice]
                    i += 1
                    print(f"现在是第i条")
                    print(data)
                    data_list.append(data)
            return data_list
        
    
        def save_to_excel(self):
            datas = self.parse_data() #数据,也就是上面获取到的嵌套列表
            print(开始保存...)
            # 表头
            excel_header = [标题,位置,户型,总价,单价]
    
            # 创建excel表格并写入表头
            wb = openpyxl.Workbook()
            ws = wb.create_sheet(title=链家长沙房源, index=0)
            wb.remove(wb[Sheet]) # 删除原来的表
            ws.append(excel_header) # 写入表头       
    
            # 遍历数据
            for data in datas:
                # 将每行数据追加到表格
                ws.append(data)
    
            # 保存表格
            wb.save(链家长沙房源.xlsx)
        """ 3. 把上面方法整合起来完成业务逻辑"""
        def run(self):
            self.parse_data()  # 获取网页信息
            self.save_to_excel()
            print("保存完成。")
          
    
    
    if __name__ == __main__:    
        spider = LianJian_changsha()
        spider.run()
    
    
  • 效果如图。

3.5 pyquery方法

  • 这次保存数据信息,我用MySQL的方法,以进一步掌握数据库的使用方法。

3.5.1安装MySQL

  • 这里登录https://dev.mysql.com/downloads/mysql/,选择Microsoft Windows打开如下页面,点击下面的Download按钮。

  • 这是一个免安装的数据库程序,然后把数据库包解压到E盘,如图。

  • 其中,my.ini是自己创建的配置文件,保存到数据库当前目录,内容如下:

    [client]
    # 设置mysql客户端默认字符集
    default-character-set=utf8
     
    [mysqld]
    # 设置3306端口
    port = 3306
    # 设置mysql的安装目录
    basedir=E:\\mysql8.0
    # 设置 mysql数据库的数据的存放目录,MySQL 8+ 不需要以下配置,系统自己生成即可,否则有可能报错
    # datadir=E:\\mysql8.0\\\\sqldata
    # 允许最大连接数
    max_connections=20
    # 服务端使用的字符集默认为8比特编码的latin1字符集
    character-set-server=utf8
    # 创建新表时将使用的默认存储引擎
    default-storage-engine=INNODB
    
  • 接下来我们来启动下 MySQL 数据库:

  • 管理员身份打开 cmd 命令行工具,切换目录:

3.5.1 初始化数据库

~~~
mysqld --initialize --console
~~~
...
2023-01-04T02:35:05.464644Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: APWCY5ws&hjQ
...
  • 安装数据库,下面也是在bin目录下执行。

    mysqld install
    
  • 安装结束后启动服务即可, 启动服务命令如下:

    net start mysql
    
  • 登陆MySQL

    当 MySQL 服务已经运行时, CD到bin目录,打开命令提示符, 输入以下格式的命名:

    mysql -h 主机名 -u 用户名 -p	
    
    • -h : 指定客户端所要登录的 MySQL 主机名, 登录本机(localhost 或 127.0.0.1)该参数可以省略;
    • -u : 登录的用户名;
    • -p : 告诉服务器将会使用一个password来登录, 如果所要登录的用户名password为空, 可以忽略此选项。
  • 查看数据库

    语法:

    show databases;
    
  • 创建数据库

    语法:

    create database 库名;
    

    这里创建一个名字为lianjia的库名。

  • 创建数据表,下面命令,可以在console窗口Mysql提示符下录入,也可以在idle的sql控制台下录入,都可以。 语法:

    create table lianjia_cs
    (
    	id  int auto_increment,
        title     varchar(100) null,
        position  varchar(200) null,
        houseInfo varchar(200) null,
        priceInfo varchar(50)  null,
        unitPrice varchar(50)  null,
       	primary key(id)
    )DEFAULT CHARSET=utf8;
    

3.5.3 数据库与idle的连接操作

  • 用VSCode连接MySQL8.0,步骤如下: (1)点扩展插件,输入MySQL,安装第一个,如图。 (2)打开左侧资源管理器,点+号,分别录入host、user、password、port,最后一个回车即可,我这里对应的是localhost、root、root、3306。然后就轻松建立一个localhost连接了。 注意:如果连接不成功,下面有Error提示的话,一般要更新一下password,才能连接。命令如下: alter user ‘root’@‘localhost’ identified with mysql_native_password by ‘root’;
  • 用PyCharm连接数据库的方法,相对也很简单。 (1)单击右侧数据库,再点+号,再点MySQL即可建立。 (2)在弹出的对话框中,输入相应的参数。

3.5.4 程序全部代码

# -*- coding:utf-8 -*-
"""
    # @Time:2022/12/19 0019 13:53
    # @Author:晚秋拾叶
    # @File:链家长沙房源.py
    # @PyCharm之Python
    # 完成“标题,位置,户型,总价,单价”几个信息的收集并保存
"""
import requests
import pymysql
from pyquery import PyQuery as pq


class LianJian_changsha(object):
    def __init__(self, num):
        self.url = https://cs.lianjia.com/ershoufang/pg/.format(num)
        self.headers = 
            User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54,

        
        # 创建数据库连接
        self.db = pymysql.Connect(
            host="localhost",
            port=3306,
            user="root",
            password="root",
            db="lianjia"
        )
        # 再创建数据库游标
        self.cursor = self.db.cursor()

     1. 发起请求并获取数据 

    def get_data_index(self):
        # 可以使用urlli库里的方法from urllib.request import Request
        # 即用urllib中的request模块里的Request类定义解析的网页数据,
        # 不过需要用read()读取二进制内容,再用decode(utf-8)转为Unicode编码
        # 所以不如requests库来得方便
        """ (1)用到第一个库requests的get方法,获取总的网页源代码 """
        resp = requests.get(self.url, headers=self.headers)
        resp.encoding = utf-8
        if resp.status_code == 200:
            return resp.text
        else:
            return None

     2. 把得到的网页文本信息进行解析 

    def parse_data_index(self, resp):
        # 进一步筛选想要的源代码内容,缩小范围
        """ (2)还是用xpath定位好范围 """
        doc = pq(resp)
        sellListContent = doc(".sellListContent li")
        for data in sellListContent.items():
            # 下面用了CSS选择器,加上text方法获取文本内容
            title = data(".info.clear .title a").text()
            position = data(".info.clear .flood").text()
            houseInfo = data(".info.clear .houseInfo").text()
            priceInfo = data(".info.clear .totalPrice.totalPrice2 span").text() + "万元"
            unitPrice = data(".info.clear .unitPrice span").text()
            # 定义sql语句,以便下面执行插入操作
            sql = "insert into lianjia_cs (title,position,houseInfo,priceInfo,unitPrice) values(%s,%s,%s,%s,%s);"
            # 把信息字段写入参数
            params = [(title, position, houseInfo, priceInfo, unitPrice)]
            # 执行语句
            self.cursor.executemany(sql, params)
            # 提交数据库
            self.db.commit()

    """ 3. 把上面方法整合起来完成业务逻辑"""

    def run(self):
        resp = self.get_data_index()  # 获取网页信息
        self.parse_data_index(resp)


if __name__ == __main__:
    # 爬取前5页的数据
    for n in range(1, 6):
        spider = LianJian_changsha(n)
        spider.run()

  • 结果如图。

3.6 parsel方法

  • 这次用parsel方法,把这个任务完成。这个方法,其实很简单,提取到网页源代码后,还是应用了xpath方法。不过这次数据,我再用MongoDB数据库保存。

3.6.1 安装MongoDB

  • 安装包下载地址:https://www.mongodb.com/try/download/community

  • step1:打开安装包直接点击Next

  • step2:继续点击Next

  • step3:点击自定义安装

  • step4:选择好安装路径,点击Next

  • step5:点击Next

  • step6:取消可视化界面勾线,直接点击Next安装

3.6.2 初始化MongoDB

  • step1:配置环境变量,找到MongoDB安装路径下的bin目录

  • step2:计算机--右击--属性--高级系统设置--环境变量--系统变量--path--新建,将bin目录复制进去即可 补充:进到data目录里面,新建两个文件夹,一个是db,一个是log

  • step3:打开cmd,输入mongod -dbpath "F:\\MongoDB\\data\\db" -logpath "F:\\MongoDB\\data\\log\\mongo.log"

  • step4:重新打开一个cmd窗口,输入mongo来启动MongoDB shell 端 这里遇到一个问题就是,我安装的是高版本的MongoDB,显示mongo命令不存在。 查了一番资料,才明白,高版本已经没有这个命令了。然后根据资料提示,重新下载一个mongosh文件代替。 地址:https://www.mongodb.com/try/download/shell

  • 下载解压后,找到bin目录,把这两个文件,复制粘贴到MongoDB下的bin目录。

  • 再执行mongosh命令就行了。

  • **step5: 创建一个数据库,再建立一个集合 ** 创建\\删除数据库

    use db_lianjia
    # 如果数据库不存在,则创建数据库,否则切换到指定数据库。
    
    db.dropDatabase()
    # 删除数据库之前,先进入数据库,之后执行
    # 删除当前数据库
    

    实例:

    >use db_lianjia
    switched to db db_lianjia
    >db
    db_lianjia
    

    如果你想查看所有数据库,可以使用 show dbs 命令:

    > show dbs
    admin   0.000GB
    config  0.000GB
    local   0.000GB
    

    可以看到,我们刚创建的数据库 db_lianjia并不在数据库的列表中, 要显示它,我们需要向 db_lianjia 数据库插入一些数据。

    增删改查操作

    MongoDB中的一张表被称为一个集合

    插入数据

    • 语法:
    # db.集合名.insert()  数据格式为json
    db.demo.insert(name:"坤哥")
    #  "_id" : ObjectId("63465fb77811f81334940270"), "name" : "坤哥" 
    

    上面的命令执行完,就自动创建了一个demo表。总结上面的命令如下图:

    • 我后面接着连接数据库,进行一个集合的实例,进一步理解上面的操作。

3.6.3 数据库与IDLE的连接操作

  • 连接MongoDB时,需要使用PyMongo库里面的MongoClient方法,格式如下:
    import pymongo
    client = pymongo.MongoClient(host=localhost, port=27017)
    
  • 上面语法用来创建MongoDB的连接对象。
  • (1)PyCharm下安装MongoDB插件。
    • 点击右侧的数据库,一步步点击

    • 下一步,输入名称:MongoDB,主机:localhost,再点击下方的“下载”,最后完成测试连接。

      注意:这里反复下载提示重试,最后发现,需要github账号。

    • 终于下载并连接成功了。

  • (2)再用VSCode连接一下。
    • 点扩展,再搜索mongo,如图下载MongoDB for VS Code:

    • 再点左侧出现的数据库,然后点CONNECTIONS右侧的+号。

    • 出现New connection之后,输入localhost,再输入账号、pwd以及本地数据库的名称。

  • 提示,账号密码的设置在管理员console命令中,分如下几步完成。
    • mongosh   # 打开MongoDB;
    • use db_lianjia   # 创建或打开数据库;
    • db.createUser(user: "root", pwd: "root", roles: [role: "root", db: "admin"])   # 修改用户和密码
    • 最后也完成VSCode下的MongoDB的连接。

3.6.4 程序全部代码

# -*- coding:utf-8 -*-
"""
    # @Time:2022/12/19 0019 13:53
    # @Author:晚秋拾叶
    # @File:链家长沙房源.py
    # @PyCharm之Python
    # 完成“标题,位置,户型,总价,单价”几个信息的收集并保存
"""
import requests
import pymongo
from parsel import Selector


class LianJian_changsha(object):
    def __init__(self, num):
        self.url = https://cs.lianjia.com/ershoufang/pg/.format(num)
        self.headers = 
            User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54,
        
        # 创建数据库连接
        self.client = pymongo.MongoClient(host="localhost", port=27017)
        # 再创建一个数据库,下面命令执行的时候,是没有创建,有则连
        self.db = self.client["db_lianjia"]

     1. 发起请求并获取数据 

    def get_data_index(self):
        # 可以使用urlli库里的方法from urllib.request import Request
        # 即用urllib中的request模块里的Request类定义解析的网页数据,
        # 不过需要用read()读取二进制内容,再用decode(utf-8)转为Unicode编码
        # 所以不如requests库来得方便
        """ (1)用到第一个库requests的get方法,获取总的网页源代码 """
        resp = requests.get(self.url, headers=self.headers)
        resp.encoding = utf-8
        if resp.status_code == 200:
            return resp.text
        else:
            return None

     2. 把得到的网页文本信息进行解析 

    def parse_data_index(self, resp):
        # 进一步筛选想要的源代码内容,缩小范围
        """ 
        (2)如同前面章节提到的,SelectorList对象再用CSS选择器选择的属性,
            先是被转成了xpath,所以这里还是用xpath定位匹配文本 
        """
        # 创建一个Selector对象
        st = Selector(resp)
        # 创建一个SelectorList对象
        sellListContent = st.css(".sellListContent li")
        i = 0
        for data in sellListContent:
            title = data.xpath(.//div[@class="title"]/a/text()).get()
            position = ",".join(data.xpath(./div[1]/div[2]//a/text()).getall())
            houseInfo = data.xpath(.//div[@class="houseInfo"]/text()).get()
            priceInfo = data.xpath(.//div[contains(@class,"totalPrice")]/span/text()).get() + "万元"
            unitPrice = data.xpath(.//div[@class="unitPrice"]/span/text()).get()

            yield 标题: title, 位置: position, 户型: houseInfo, 总价: priceInfo, 单价: unitPrice
           

    """ 3. 把上面方法整合起来完成业务逻辑"""

    def run(self):
        resp = self.get_data_index()  # 获取网页信息
        parse_data = self.parse_data_index(resp)
        for item in parse_data:
            print(item)
            self.db.lianjia_cs.insert_one(item)


if __name__ == __main__:
    # 爬取前5页的数据
    for n in range(1, 6):
        spider = LianJian_changsha(n)
        spider.run()

  • 用db命令也能查看数据已经写入。

4. 面向对象的写法总结

  • 面向对象的编程,能够让你的思路条理化。上面几种爬虫库完成既定目标,都是基于这个思想。
  • 首先,都是建立一个类,包含要抓取的网页的地址,伪装和各种数据库的连接等等。
  • 再写几个重要的方法。分别是获取网页数据、用各种爬虫库里的方法对网页数据进行解析、写入各种形式的保存方式的类方法、整合运行的类方法、调用类的主程序。。。

4.1 获取网页数据

  • 这部分代码,所有的爬虫库都用到requests里的get方法,不过,遇到大型数据的爬取,就有点力不从心了。好在后面还会学到更强大的处理方法。
  • 这里不管是得到的是什么数据,都要保证返回不为空,否则后面的工作也就成空了。

4.2 网页数据解析提取

  • 这个题目中的要求,基本没有字典形式的数据,也没有抓取图像和音视频的情况,如果有的话,自然用xpath和bs4更方便一些。

4.3 遍历源代码数据列表

  • 这个题目中,网页数据解析的时候,只是缩小范围,再得到列表,用遍历的for语句,结合各种方法得到一条一条的数据了。至于什么时候用return返回数据,还是用yield生成器,则要看保存的形式和地方了。

4.4 数据保存的问题解决

  • 保存的方式,从简单的控制台打印,到文本文档,csv格式,再到稍复杂的csv和excel格式,一直到两种数据库MySQL和MongoDB的形式,一遍一遍改写和测试,加上我这新手不熟悉不断的找寻各种问题解决的资料信息,一共花费了近4天的时间,终于完成这部分知识的笔记。

4.5 业务逻辑的实现

  • 再总结一个问题,就是业务逻辑的实现。主要是在run方法里面完成,翻页的效果,除了bs4这个方法结果保存为excel表格要处理翻页,其它的都在main主函数里面for循环完成。
  • bs4这段中,为了追加成功,把这个功能写到了解析的类方法parse_data()里面,然后少了写的方法write_data()了。

4. 总结

  • 上面写得太多,不总结了。总结一句,就是学习的过程,如柳暗花明,如众里寻他,如漫漫轻云,又如南朝四百八十寺,更如春风又绿江南岸。

以上是关于关于爬虫中几个常用库的使用方法总结的主要内容,如果未能解决你的问题,请参考以下文章

js 怎么实现,数量*重量*单价=总价?帮忙看一下需要怎么改?

用js实现商品购买数量越多,商品单价越低,并计算总价?

编写一个程序,输入货物的数量及单价,求总价并输出。如何编写程序?

如何用JS算出商品总价!

用javascript做计算水果的总价

localStorage实现购物车数量单价和总价实时同步