精华内容
下载资源
问答
  • python爬虫当当网.zip

    2021-02-22 17:51:24
    python爬虫 scrapy框架爬虫当当网 mysql进行数据存储 pyecharts进行数据分析 最终网页呈现
  • 因为淘宝网需要登录,而京东的html比较奇怪,所以最后选取了当当网 #CrowDangDangPrize.py import requests import re import urllib def getHTMLText(url): try: r=requests.get(url,timeout=30) r.raise_for_...

    因为淘宝网需要登录,而京东的html比较奇怪,所以最后选取了当当网

    技术路线

    requests——re

    #CrowDangDangPrize.py
    import requests
    import re
    import urllib
    def getHTMLText(url):
        try:
            r=requests.get(url,timeout=30)
            r.raise_for_status()
            r.encoding=r.apparent_encoding
            return r.text
        except:
            return ""
    
    def parsePage(ls,html):
        try:
            pls=re.findall(r'now\_price\"\>\&yen\;[\d\.]+',html)
            nls=re.findall(r"alt\=\'.*?\'",html)
            for i in range(len(pls)):
                price=eval(pls[i].split(';')[1])
                name=eval(nls[i].split("=")[1])
                ls.append([price,name])
        except:
            print("")
    
    def printGoodList(ls):
        print("{0:^4}\t{1:^8}\t{2:^30}".format("序号","价格","商品名称"))
        count=1
        for g in ls:
            print("{0:^4}\t{1:^8}\t{2:^30}".format(count,g[0],g[1]))
            count=count+1
    
    def word2url(text):
        text=text.encode('gb2312')
        text=urllib.parse.quote(text)
        return text    
    def main():
            good ='同济'
            good=word2url(good)
            depth=1
            start_url='http://search.dangdang.com/?key='+good
            infolist=[]
            for i in range(1,depth+1):
                try:
                    url=start_url+'&act=input&page_index='+str(i)
                    html=getHTMLText(url)
                    parsePage(infolist,html)
                except:
                    continue
            printGoodList(infolist)
    main()
    

    在这个案例中,我们使用正则表达式在文本文件中查找我们需要的信息,因此我们需要在网页源代码中找到商品名称和价格独一无二的格式:
    在这里插入图片描述
    在这里插入图片描述
    一页中有60款商品,最后敲定正则表达式:

    价格:r'now\_price\"\>\&yen\;[\d\.]+'
    名称:r"alt\=\'.*?\'"
    

    通过re.findall()返回的是列表类型,其中包含着我们不需要的信息,因此我们要采取分片处理

    for i in range(len(pls)):
                price=eval(pls[i].split(';')[1])
                name=eval(nls[i].split("=")[1])
    

    还有一个需要注意的是,当搜索框中输入’同济’后,网址中并没有出现’同济’,而是

    %CD%AC%BC%C3
    

    这就需要我们对文字进行编码(gb2312):

    def word2url(text):
        text=text.encode('gb2312')
        text=urllib.parse.quote(text)
        return text 
    

    运行结果:

    序号 	   价格   	             商品名称             
     1  	  36.5  	         高等数学(第七版)(上册)        
     2  	  30.2  	         高等数学(第七版)(下册)        
     3  	  16.3  	 同济大学数学系 高等数学(第七版下册)同步辅导 习题全解 考研精粹(同济大学《高等数学》第七版理工类同步辅导、考研数学
     4  	  19.0  	 同济大学数学系 高等数学(第七版上册)同步辅导 习题全解 考研精粹(同济大学《高等数学》第七版理工类同步辅导、考研数学
     5  	  29.7  	      同济大学数学系列教材 概率论与数理统计     
     6  	  17.1  	 同济大学数学系 工程数学 线性代数(第六版)同步辅导 习题全解 考研精粹(同济第六版配套辅导习题集、考研数学指定参考书
     7  	  31.4  	      同济大学数学系列教材 高等数学(上册)     
     8  	  25.6  	     同济大学数学系列教材 高等数学习题全解上册    
     9  	  17.5  	        同济大学数学系列教材 线性代数       
     10 	  18.3  	         工程数学 线性代数 第六版        
     11 	  31.8  	      同济大学数学系列教材 高等数学(下册)     
     12 	  26.2  	     高等数学习题全解指南(下册)同济 第七版     
     13 	  33.4  	      高等数学习题全解指南(上册)同济第六版     
     14 	  21.7  	         工程数学 线性代数 第五版        
     15 	  45.1  	          高等数学 第六版 上册         
     16 	  29.3  	          高等数学 第六版 下册         
     17 	  27.0  	     高等数学习题全解指南(下册)同济.第六版     
     18 	  29.5  	 现代数值计算(2)(工业和信息化部“十二五”规划教材)
     19 	  15.7  	    高等数学 同济六版 习题全解与考研指导(下册)   
     20 	  46.5  	           理论力学(第3版)          
     21 	  21.2  	   线性代数同济六版 线性代数辅导 同济六版 张天德   
     22 	  29.4  	   高等数学附册――学习辅导与习题选解(同济・第7)  
     23 	  34.6  	       高等数学 第六版 下册(换封面)       
     24 	  16.0  	        现代数值计算习题指导(2)       
     25 	  28.7  	     高等数学习题全解指南(上册)同济 第七版     
     26 	  21.0  	           概率论与数理统计           
     27 	  28.7  	          高等数学(第3版)上册         
     28 	  26.4  	       高等数学及其应用(第二版)(下册)      
     29 	  15.5  	          工程数学线性代数第五版         
     30 	  20.5  	   概率统计简明教程附册 学习辅导与习题全解(第二版)  
     31 	  17.7  	    线性代数附册 学习辅导与习题全解 同济 第六版   
     32 	  25.9  	          高等数学(第3版)下册         
     33 	 101.1  	         同济大学建筑设计院6034 	  12.6  	           高等数学习题册.35 	  30.9  	          高等数学典型例题分析          
     36 	  21.1  	         高等数学(第四版)(下册)        
     37 	  29.6  	       高等数学及其应用(第二版)(上册)      
     38 	  17.8  	         高等数学(上册)(第二版)        
     39 	  22.0  	       高等数学及其应用(第二版)(下册)      
     40 	  21.7  	         高等数学(下册)(第2版)        
     41 	  20.5  	             应用统计             
     42 	  47.4  	  同济博士论丛――复杂高层结构的整体抗震试验与非线性分析 
     43 	  11.5  	      线性代数习题超精解 同济六版 张天德      
     44 	  20.8  	   高等数学同步辅导(上册)――配合同济七版高等数学   
     45 	 118.5  	      同济博士论丛――住宅工业化发展脉络研究     
     46 	  95.0  	 高等数学同济第七版 高等数学教材上下册 同步辅导及习题全解 全套4本 同济大学第七版 高等教育出版社 2019广东省插
     47 	  91.9  	 同济高数 高等数学(第七版)教材 同步辅导及习题全解上下册4本 同济七版 第六版改版 448 	  69.5  	 同济博士论丛――城市污泥强化深度脱水资源化利用及卫生填埋末端处置关键技术研究
     49 	 124.8  	   同济博士论丛――中国城市群空间结构与集合能效研究   
     50 	  39.5  	  同济博士论丛――基于Petri网精炼的系统建模与分析  
     51 	  98.7  	 同济博士论丛――线性文化景观的保护与发展研究――基于景观性格理论
     52 	  22.6  	 星火燎原高数高等数学辅导及习题精解同济七版上册配同济大学7版教材
     53 	  42.6  	 同济博士论丛――高钙粉煤灰基地聚合物及固封键合重金属研究 
     54 	  53.0  	 同济大学 结构力学 朱慈勉 第3版 上下册 高等教育出版社 朱慈勉结构力学第三版 结构力学教材考研指定用书普通物理实验
     55 	  93.2  	   同济博士论丛――基于游憩理论的城市开放空间规划研究  
     56 	  40.2  	 同济博士论丛――铁氧体/碳纤维复合材料吸波性能及涂层结构设计
     57 	  56.0  	  同济博士论丛――基于非铂催化剂的质子交换膜燃料电池研究 
     58 	  69.5  	 同济博士论丛――机载激光数据辅助的高光谱遥感影像面向对象分类和精度分析
     59 	  37.2  	 同济复兴古典书院丛书:心术与笔法:虞世南《笔髓论》注及书画讲稿
     60 	  60.8  	 同济博士论丛――基于交通网络均衡理论的交通需求管理政策评价研究
    
    展开全文
  • python爬虫登陆当当网,给出了旋转验证码的解决方法,也给出了使用第三方登陆绕过旋转图片验证码。

    在假期接的python爬虫设计之一是实现当当网的登陆和获取一些数据。
    在初期打开官网并想登陆时发发现在这里插入图片描述有一个安全提醒标签,继续下一步后
    在这里插入图片描述
    可以发现是需要旋转的图片验证码。
    开始正题,我们需要如何去解决旋转图片验证码呢,这方面的资料实在是太少了,但是大概的思路就是两个,第一个是能够知道每张图片需要点击的次数,然后通过程序去实现,第二个是想办法绕过图片验证码。
    第一种方案很幸运找到了一位前辈的博客,给出了解决办法:
    图片旋转验证码
    在程序复现时会发现在程序复现时不能成功,这是因为这个网站在维护时添加了一个确认界面,只有当点击后才能进入真正的登陆界面,而且一些程序内关键元素的定位方式也发生了一些改变(极少数)。见下图:
    在这里插入图片描述
    在这里插入图片描述
    这两幅图是不是十分眼熟,和当当网可以说一摸一样,所以我做的第一步就是复现这个历程,登陆这个漫画网站。我在前辈的代码基础上做过改变的代码如下(这真的是站在巨人的肩膀上,真的是万分感谢):

    #样本采集
    from selenium import webdriver
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.common.by import  By
    from PIL import Image
    import random
    from io import BytesIO
    import time
    
    
    class Crack(object):
        def __init__(self, start_number, count):
            self.login_url = "http://www.1kkk.com/"
            self.start_number = start_number
            self.count = count
            self.chrome_options = webdriver.ChromeOptions()#无弹窗
            self.chrome_options.add_argument("--healess")
            self.browser = webdriver.Chrome()
            self.browser.maximize_window()
            self.wait = WebDriverWait(self.browser, 30)
    
        def login(self):
            """
            输入账号,密码
            :return:None
            """
            self.browser.get(self.login_url)#进入登陆界面
            self.browser.find_element_by_class_name("header-avatar").click()#点击右侧灰色人头像,打开登陆界面
            self.browser.find_element_by_xpath("//*[@id='sy-win']/div/div[4]/a[2]").click()
            #self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "overflow auto"))).click()
            print("成功点击")
            # 获取所有图片
            for num in range(self.start_number, self.start_number+self.count):
                self.image_png(num)#通过获取网页截图,然后进行切片,返回四张图片
                self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "rotate-refresh"))).click()#不停的点击换一组,下载至足够多的数据
                time.sleep(0.5)
    
        def save_screen_png(self):
            """
            获取网页截图
            :return: 截图对象
            """
            self.wait.until(EC.presence_of_all_elements_located((By.CLASS_NAME, "rotate-background")))
            screen_image = self.browser.get_screenshot_as_png()
            screenshot = Image.open(BytesIO(screen_image))
            screenshot.save("screenshot{}.png".format(random.randint(1, 5)))
            return screenshot
    
        def image_png(self, num):
            """
            通过获取网页截图,然后进行切片,返回四张图片
            :return:
            """
            screenshot = self.save_screen_png()
            images = []
            for num_2 in range(1, 5):
                # 依次获取5张图片,存入iamges列表中
                images.append(self.get_image_position(screenshot, num, num_2))
            # 获取整体四张图片的坐标
            # 进行切片
    
        def get_image_position(self, screenshot, number, number_2):
            """
            获取四张图片的下标
            :return: left, top, right, bottom
            """
            image = self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='rotate-background'][{}]".format(number_2))))
            location = image.location
            size = image.size
            top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
                'width']
            image = screenshot.crop((left, top, right, bottom))
            image.save("./static/total_images/image{}_{}.png".format(number, number_2))
            return image
    
        def __del__(self):
            self.browser.quit()
    
    
    def download(start_number, count):
        """
        初始化登录类,下载图片
        :param start_number:开启位置
        :param count: 数量
        :return:
        """
        c = Crack(start_number, count)#初始化
        c.login()#调用c(即Crack)中的login()函数
        del c
    
    
    def main():
        download(1, 1000)
    
    
    if __name__ == '__main__':
        main()
    
    
    #图片去重
    from PIL import Image
    import os
    import gevent
    from gevent import monkey
    
    monkey.patch_all()
    
    # 图片数量
    gCount = 0
    
    # 列表,用来保存rgb
    rgb_dif_list = []
    
    # 当前保存图片的名称
    gNumber = 0
    
    
    def sum_rgb(image):
        """
        计算rgb的值
        :param images: 图片
        :return: rgb的值
        """
        num = 0
        for i in range(image.size[0]):
            for y in range(image.size[1]):
                pixel = image.load()[i, y]
                num = num + image.load()[i, y][0] + image.load()[i, y][1] + image.load()[i, y][2]
        return num
    
    
    def check_have_in(num):
        """
        通过rgb的总值,来判断是否已经存在列表
        :param num: Ture or False
        :return: 
        """
        global rgb_dif_list
        if num in rgb_dif_list:
            # 如果存在,就得删除
            return True
        else:
            # 否则就将rgb存入列表中,更改名字,并返回False
            return False
    
    
    def delete(image_url):
        """
        删除图片
        :param image_url: 图片的url
        :return:
        """
        print("删除图片:", image_url)
        os.remove(image_url)
    
    
    def start_check(start_number, count):
        global rgb_dif_list
        global gCount
        global gNumber
        images_url = "./static/total_images/{}"
        save_url = "./static/images/{}"
        for number_1 in range(start_number, start_number + count):
            for number_2 in range(1, 5):
                image_url = images_url.format("image{}_{}.png".format(number_1, number_2))
                if os.path.isfile(image_url):
                    image = Image.open(image_url)
                    # 通过元素的rgb三个值相加的总数,通过列表保存,如果在列表中存在就添加,否则就删除
                    rgb_num = sum_rgb(image)
                    print("image{}_{}.png".format(number_1, number_2), rgb_num)
                    # 判断该图片的rgb是否已经存在列表中
                    if rgb_num > 4000000:
                        continue
                    for num in range(rgb_num - 3000, rgb_num + 3000):
                        check_result = check_have_in(num)
                        # 判断结果,做响应处理
                        if check_result:
                            # 存在情况,退出
                            break
                    else:
                        rgb_dif_list.append(rgb_num)
                        gCount += 1
                        # 不存在情况,更改名字
                        gNumber += 1
                        image.save(save_url.format("images{}.png".format(gNumber)))
        if start_number + count == 501:
            print("剩余图片总数为", gCount)
    
    
    def main():
        gevent.joinall([
            gevent.spawn(start_check, 1, 100),
            gevent.spawn(start_check, 101, 100),
            gevent.spawn(start_check, 201, 100),
            gevent.spawn(start_check, 301, 100),
            gevent.spawn(start_check, 401, 100),
        ])
        # start_check(1, 10)
    
    
    if __name__ == "__main__":
        main()
    
    
    #验证码破解
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.common.exceptions import TimeoutException
    import time
    import os
    from PIL import Image
    from io import BytesIO
    
    
    class Crack(object):
        def __init__(self):
            self.login_url = "http://www.1kkk.com/"
            # self.chrome_options = webdriver.ChromeOptions()
            # self.chrome_options.add_argument("--healess")
            self.browser = webdriver.Firefox()
            self.browser.maximize_window()
            self.wait = WebDriverWait(self.browser, 5)
            self.browser.get(self.login_url)
            time.sleep(1)
    
        def login(self):
            """
            输入账号,密码
            :return:None
            """
            try:
                self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "header-avatar"))).click()#点击右侧灰色人头像,打开登陆界面
                self.browser.find_element_by_xpath("//*[@id='sy-win']/div/div[4]/a[2]").click()#点击同意按钮
            except TimeoutException:#超时则再次请求登陆
                self.browser.refresh()
                self.login()
                return
            #输入账号和密码
            name_page = self.browser.find_element_by_name("txt_name")
            name_page.send_keys("18218299414")
            password_page = self.browser.find_element_by_name("txt_password")
            password_page.send_keys("shao0812")
            true_or_false = True
            while true_or_false:
                true_or_false = False
                # 获取四张需要旋转的图片
                images = self.image_png()
                # 获取整体四张图片的几次
                turn_num_list = []
                for image in images:
                    turn_num_list.append(self.image_turn_num(image))
                # print(turn_num_list)
                for i in turn_num_list:
                    if i == 5:
                        self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'rotate-refresh'))).click()
                        time.sleep(1)
                        true_or_false = True
            # 根据上面得到的旋转次数点击图片
            self.click_image(turn_num_list)
            # 结果正确,点击登录按钮
            self.click_submit()
            # 如果旋转出问题,就得重新来
            try:
                if self.browser.find_element_by_xpath("/html/body/section[3]/div/div/div/div/div/div[1]/label"):
                    # 如果登录不成功,将重新刷新页面登录
                    self.browser.refresh()
                    self.login()
                time.sleep(1)
            except:
                pass
    
        def click_image(self, turn_num_list):
            """
            通过算出来的点击次数,来点击图片
            :param turn_num_list: 四张图需要点击的次数
            :return: None
            """
            for i in range(0, len(turn_num_list)):
                if turn_num_list[i] == 0:
                    continue
                image = self.wait.until(
                    EC.presence_of_element_located((By.XPATH, "//div[@class='rotate-background'][{}]".format(i+1))))
                for _ in range(turn_num_list[i]):
                    image.click()
                    time.sleep(0.5)
    
        def save_screen_png(self):
            """
            获取网页截图
            :return: 截图对象
            """
            screen_image = self.browser.get_screenshot_as_png()
            screenshot = Image.open(BytesIO(screen_image))
            # screenshot.save("screenshot.png")
            return screenshot
    
        def image_png(self):
            """
            通过获取网页截图,然后进行切片,返回四张图片
            :return:
            """
            screenshot = self.save_screen_png()
            images = []
            for num in range(1, 5):
                # 依次获取4张图片,存入iamges列表中
                images.append(self.get_image(screenshot, num))
            return images
    
        def get_image(self, screenshot, number):
            """
            获取四张图片的下标
            :return: left, top, right, bottom
            """
            image = self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='rotate-background'][{}]".format(number))))
            location = image.location
            size = image.size
            top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
                'width']
            image = screenshot.crop((left, top, right, bottom))
            # image.save("image{}.png".format(number))
            return image
    
        def image_turn_num(self, image):
            """
            用获取的图片跟图片库的图片比较,
            :param image: 原图
            :return:
            """
            for i in range(0, 4):
                # 原图最多转三次
                dir_path = "./static/images/"
                change_image = image.rotate(-90*i)
                # change_image.save("change{}.png".format(i))
                for or_path in os.listdir(dir_path):
                    or_image = Image.open(os.path.join(dir_path, or_path))
                    result = self.examine_pixel(or_image, change_image)
                    if result:
                        return i
            return 5
    
        def examine_pixel(self, image1, image2):
            """
            判断来个图片是否相等
            :param image1: 图片1
            :param image2: 图片2
            :return:
            """
            thredhold = 100
            for x in range(image1.size[0]):
                for y in range(image1.size[1]):
                    pixel1 = image1.load()[x, y]
                    pixel2 = image2.load()[x, y]
                    if not (abs(pixel1[0] - pixel2[0]) < thredhold and abs(pixel1[1] - pixel2[1]) < thredhold and abs(pixel1[2] - pixel2[2]) < thredhold):
                        return False
            return True
    
        def click_submit(self):
            """
            点击登录按钮
            :return: None
            """
            submit = self.wait.until(EC.element_to_be_clickable((By.ID, "btnLogin")))
            submit.click()
    
        def __del__(self):
            self.browser.quit()
    
    
    def main():
        """pass"""
        c = Crack()
        c.login()
    
    
    if __name__ == "__main__":
        main()
    
    

    至此就实现了漫画网的登陆。
    接下来我们需要将代码更改,更改就是根据当当网的网页源代码更改元素定位在程序中的定位方式,这里实现了采集样本,还有样本去重,但是在破解时没有用到采集的样本,是直接看每个验证码需要旋转多少次,然后手工输入每张验证码需要旋转的次数,至于原因我在后面进行叙述。

    #当当采样
    from selenium import webdriver
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.common.by import  By
    from PIL import Image
    import random
    from io import BytesIO
    import time
    #报错selenium.common.exceptions.WebDriverException: Message: 'geckodriver' executable needs to be in PATH.的处理办法如下
    #参考https://blog.csdn.net/sisteryaya/article/details/75257681
    
    class Crack(object):
        def __init__(self, start_number, count):
            self.login_url = "http://www.dangdang.com/?_utm_brand_id=10934&_ddclickunion=621-pz-%CD%B7%B2%BF_%D6%F7%B1%EA%CC%E2_%B5%B1%B5%B1%B9%D9%CD%F8%2C%D5%FD%C6%B7%B5%CD%BC%DB%2C%C6%B7%D6%CA%B1%A3%D6%A4%2C30%CD%F2%CD%BC%CA%E9%C3%BF%C2%FA100%BC%F540%A3%A1|ad_type=0|sys_id=1"
            self.start_number = start_number
            self.count = count
            self.chrome_options = webdriver.ChromeOptions()#无弹窗   网页包含"href=”javascript:void(0);"必须使用Chrome浏览器,否则登陆界面的“安全提醒”弹出界面中的“知道了”按钮没办法点击,会报错
            self.chrome_options.add_argument("--healess")
            self.browser = webdriver.Chrome()
            self.browser.maximize_window()
            self.wait = WebDriverWait(self.browser, 30)
    
        def login(self):
            """
            输入账号,密码
            :return:None
            """
            self.browser.get(self.login_url)#进入登陆界面
            self.browser.find_element_by_class_name("login_link").click()#点击"登陆按钮",打开登陆界面
            self.browser.find_element_by_xpath('//*[@id="J_loginMaskClose"]').click()# 点击知道了按钮
            print("成功点击")
            time.sleep(2)
            # 获取所有图片
            for num in range(self.start_number, self.start_number+self.count):
                self.image_png(num)#通过获取网页截图,然后进行切片,返回四张图片
                self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "Rotate-refresh"))).click()#不停的点击换一组,下载至足够多的数据
                time.sleep(0.5)
    
        def save_screen_png(self):
            """
            获取网页截图
            :return: 截图对象
            """
            self.wait.until(EC.presence_of_all_elements_located((By.CLASS_NAME, "Rotate-background")))
            screen_image = self.browser.get_screenshot_as_png()
            screenshot = Image.open(BytesIO(screen_image))
            screenshot.save("screenshot{}.png".format(random.randint(1, 5)))
            return screenshot
    
        def image_png(self, num):
            """
            通过获取网页截图,然后进行切片,返回四张图片
            :return:
            """
            screenshot = self.save_screen_png()
            images = []
            for num_2 in range(1, 5):
                # 依次获取5张图片,存入iamges列表中
                images.append(self.get_image_position(screenshot, num, num_2))
            # 获取整体四张图片的坐标
            # 进行切片
    
        def get_image_position(self, screenshot, number, number_2):
            """
            获取四张图片的下标
            :return: left, top, right, bottom
            """
            image = self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='Rotate-background'][{}]".format(number_2))))
            location = image.location
            size = image.size
            top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
                'width']
            image = screenshot.crop((left, top, right, bottom))
            image.save("./static/total_images/image{}_{}.png".format(number, number_2))
            return image
    
        def __del__(self):
            self.browser.quit()
    
    
    def download(start_number, count):
        """
        初始化登录类,下载图片
        :param start_number:开启位置
        :param count: 数量
        :return:
        """
        c = Crack(start_number, count)#初始化
        c.login()#调用c(即Crack)中的login()函数
        del c
    
    
    def main():
        download(1, 10000)
    
    
    if __name__ == '__main__':
        main()
    
    
    #当当样本去重
    from PIL import Image
    import os
    import gevent
    from gevent import monkey
    
    monkey.patch_all()
    
    # 图片数量
    gCount = 0
    
    # 列表,用来保存rgb
    rgb_dif_list = []
    
    # 当前保存图片的名称
    gNumber = 0
    
    
    def sum_rgb(image):
        """
        计算rgb的值
        :param images: 图片
        :return: rgb的值
        """
        num = 0
        for i in range(image.size[0]):
            for y in range(image.size[1]):
                pixel = image.load()[i, y]
                num = num + image.load()[i, y][0] + image.load()[i, y][1] + image.load()[i, y][2]
        return num
    
    
    def check_have_in(num):
        """
        通过rgb的总值,来判断是否已经存在列表
        :param num: Ture or False
        :return: 
        """
        global rgb_dif_list
        if num in rgb_dif_list:
            # 如果存在,就得删除
            return True
        else:
            # 否则就将rgb存入列表中,更改名字,并返回False
            return False
    
    
    def delete(image_url):
        """
        删除图片
        :param image_url: 图片的url
        :return:
        """
        print("删除图片:", image_url)
        os.remove(image_url)
    
    
    def start_check(start_number, count):
        global rgb_dif_list
        global gCount
        global gNumber
        images_url = "./static/total_images/{}"
        save_url = "./static/images/{}"
        for number_1 in range(start_number, start_number + count):
            for number_2 in range(1, 5):
                image_url = images_url.format("image{}_{}.png".format(number_1, number_2))
                if os.path.isfile(image_url):
                    image = Image.open(image_url)
                    # 通过元素的rgb三个值相加的总数,通过列表保存,如果在列表中存在就添加,否则就删除
                    rgb_num = sum_rgb(image)
                    print("image{}_{}.png".format(number_1, number_2), rgb_num)
                    # 判断该图片的rgb是否已经存在列表中
                    if rgb_num > 4000000:
                        continue
                    for num in range(rgb_num - 3000, rgb_num + 3000):
                        check_result = check_have_in(num)
                        # 判断结果,做响应处理
                        if check_result:
                            # 存在情况,退出
                            break
                    else:
                        rgb_dif_list.append(rgb_num)
                        gCount += 1
                        # 不存在情况,更改名字
                        gNumber += 1
                        image.save(save_url.format("images{}.png".format(gNumber)))
        if start_number + count == 10001:
            print("剩余图片总数为", gCount)
    
    
    def main():
        gevent.joinall([
            gevent.spawn(start_check, 1, 100),
            gevent.spawn(start_check, 101, 100),
            gevent.spawn(start_check, 201, 100),
            gevent.spawn(start_check, 301, 100),
            gevent.spawn(start_check, 401, 100),
            gevent.spawn(start_check, 501, 100),
            gevent.spawn(start_check, 601, 100),
            gevent.spawn(start_check, 701, 100),
            gevent.spawn(start_check, 801, 100),
            gevent.spawn(start_check, 901, 100),
            gevent.spawn(start_check, 1001, 100),
            gevent.spawn(start_check, 1101, 100),
            gevent.spawn(start_check, 1201, 100),
            gevent.spawn(start_check, 1301, 100),
            gevent.spawn(start_check, 1401, 100),
            gevent.spawn(start_check, 1501, 100),
            gevent.spawn(start_check, 1601, 100),
            gevent.spawn(start_check, 1701, 100),
            gevent.spawn(start_check, 1801, 100),
            gevent.spawn(start_check, 1901, 100),
            gevent.spawn(start_check, 2001, 100),
            gevent.spawn(start_check, 2101, 100),
            gevent.spawn(start_check, 2201, 100),
            gevent.spawn(start_check, 2301, 100),
            gevent.spawn(start_check, 2401, 100),
            gevent.spawn(start_check, 2501, 100),
            gevent.spawn(start_check, 2601, 100),
            gevent.spawn(start_check, 2701, 100),
            gevent.spawn(start_check, 2801, 100),
            gevent.spawn(start_check, 2901, 100),
            gevent.spawn(start_check, 3001, 100),
            gevent.spawn(start_check, 3101, 100),
            gevent.spawn(start_check, 3201, 100),
            gevent.spawn(start_check, 3301, 100),
            gevent.spawn(start_check, 3401, 100),
            gevent.spawn(start_check, 3501, 100),
            gevent.spawn(start_check, 3601, 100),
            gevent.spawn(start_check, 3701, 100),
            gevent.spawn(start_check, 3801, 100),
            gevent.spawn(start_check, 3901, 100),
            gevent.spawn(start_check, 4001, 100),
            gevent.spawn(start_check, 4101, 100),
            gevent.spawn(start_check, 4201, 100),
            gevent.spawn(start_check, 4301, 100),
            gevent.spawn(start_check, 4401, 100),
            gevent.spawn(start_check, 4501, 100),
            gevent.spawn(start_check, 4601, 100),
            gevent.spawn(start_check, 4701, 100),
            gevent.spawn(start_check, 4801, 100),
            gevent.spawn(start_check, 4901, 100),
            gevent.spawn(start_check, 5001, 100),
            gevent.spawn(start_check, 5101, 100),
            gevent.spawn(start_check, 5201, 100),
            gevent.spawn(start_check, 5301, 100),
            gevent.spawn(start_check, 5401, 100),
            gevent.spawn(start_check, 5501, 100),
            gevent.spawn(start_check, 5601, 100),
            gevent.spawn(start_check, 5701, 100),
            gevent.spawn(start_check, 5801, 100),
            gevent.spawn(start_check, 5901, 100),
            gevent.spawn(start_check, 6001, 100),
            gevent.spawn(start_check, 6101, 100),
            gevent.spawn(start_check, 6201, 100),
            gevent.spawn(start_check, 6301, 100),
            gevent.spawn(start_check, 6401, 100),
            gevent.spawn(start_check, 6501, 100),
            gevent.spawn(start_check, 6601, 100),
            gevent.spawn(start_check, 6701, 100),
            gevent.spawn(start_check, 6801, 100),
            gevent.spawn(start_check, 6901, 100),
            gevent.spawn(start_check, 7001, 100),
            gevent.spawn(start_check, 7101, 100),
            gevent.spawn(start_check, 7201, 100),
            gevent.spawn(start_check, 7301, 100),
            gevent.spawn(start_check, 7401, 100),
            gevent.spawn(start_check, 7501, 100),
            gevent.spawn(start_check, 7601, 100),
            gevent.spawn(start_check, 7701, 100),
            gevent.spawn(start_check, 7801, 100),
            gevent.spawn(start_check, 7901, 100),
            gevent.spawn(start_check, 8001, 100),
            gevent.spawn(start_check, 8101, 100),
            gevent.spawn(start_check, 8201, 100),
            gevent.spawn(start_check, 8301, 100),
            gevent.spawn(start_check, 8401, 100),
            gevent.spawn(start_check, 8501, 100),
            gevent.spawn(start_check, 8601, 100),
            gevent.spawn(start_check, 8701, 100),
            gevent.spawn(start_check, 8801, 100),
            gevent.spawn(start_check, 8901, 100),
            gevent.spawn(start_check, 9001, 100),
            gevent.spawn(start_check, 9101, 100),
            gevent.spawn(start_check, 9201, 100),
            gevent.spawn(start_check, 9301, 100),
            gevent.spawn(start_check, 9401, 100),
            gevent.spawn(start_check, 9501, 100),
            gevent.spawn(start_check, 9601, 100),
            gevent.spawn(start_check, 9701, 100),
            gevent.spawn(start_check, 9801, 100),
            gevent.spawn(start_check, 9901, 100),
        ])
        # start_check(1, 10)
    
    
    if __name__ == "__main__":
        main()
    
    
    #当当验证码破解
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.common.exceptions import TimeoutException
    import time
    import os
    from PIL import Image
    from io import BytesIO
    
    
    class Crack(object):
        def __init__(self):
            self.login_url = "http://www.dangdang.com/"
            # self.chrome_options = webdriver.ChromeOptions()
            # self.chrome_options.add_argument("--healess")
            self.browser = webdriver.Chrome()
            self.browser.maximize_window()
            self.wait = WebDriverWait(self.browser, 2)
            time.sleep(0.5)
    
        def login(self):
            """
            输入账号,密码
            :return:None
            """
            try:
                self.browser.get(self.login_url)
                self.browser.find_element_by_class_name("login_link").click()  # 点击"登陆按钮",打开登陆界面
                self.browser.find_element_by_xpath('//*[@id="J_loginMaskClose"]').click()  # 点击知道了按钮
            except TimeoutException:#超时则再次请求登陆
                self.browser.refresh()
                self.login()
                return
            #输入账号和密码
            name_page = self.browser.find_element_by_name('txtUsername')
            name_page.send_keys("你的手机账号")
            password_page = self.browser.find_element_by_name('txtPassword')
            password_page.send_keys("手机账号密码")
            true_or_false = True
            turn_num_list = []
            '''
            while true_or_false:
                print("进入while")
                true_or_false = False
                print("获取四张需要旋转的图片")
                # 获取四张需要旋转的图片
                images = self.image_png()
                print(images)
                # 获取整体四张图片的几次
                turn_num_list = []
                print("进入for")
                for image in images:
                    turn_num_list.append(self.image_turn_num(image))
                print("进入第二个for")
                print("turn_num_list:",turn_num_list)
                for i in turn_num_list:
                    if i == 5:
                        self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'Rotate-refresh'))).click()#数据库没有匹配的图片点击下一组
                        time.sleep(1)
                        true_or_false = True
                print("出第二个for")
             '''
            turn_num_list.append(int(input("顺时针旋转次数")))
            turn_num_list.append(int(input("顺时针旋转次数")))
            turn_num_list.append(int(input("顺时针旋转次数")))
            turn_num_list.append(int(input("顺时针旋转次数")))
            print("根据上面得到的旋转次数点击图片")
            # 根据上面得到的旋转次数点击图片
            self.click_image(turn_num_list)
            print("结果正确,点击登录按钮")
            # 结果正确,点击登录按钮
            self.click_submit()
            #等待加载
            time.sleep(3)
            # 如果旋转出问题,就得重新来
            if self.browser.find_element_by_class_name('logo_line'):#如果成功登陆则可以检测到搜索框
                while True:
                    pass
                pass
                #print(self.browser.find_element_by_xpath('//*[@id="J_rotateVcodeWrap"]/div[1]/label[1]'))
                # 如果登录不成功,将重新刷新页面登录
            else:#登录失败则重新登陆
                self.browser.refresh()
                self.login()
            time.sleep(10)
            print("try没问题")
    
    
    
        def click_image(self, turn_num_list):
            """
            通过算出来的点击次数,来点击图片
            :param turn_num_list: 四张图需要点击的次数
            :return: None
            """
            for i in range(0, len(turn_num_list)):
                if turn_num_list[i] == 0:
                    continue
                image = self.wait.until(
                    EC.presence_of_element_located((By.XPATH, "//div[@class='Rotate-background'][{}]".format(i+1))))
                for _ in range(turn_num_list[i]):
                    image.click()
                    time.sleep(0.5)
    
        def save_screen_png(self):
            """
            获取网页截图
            :return: 截图对象
            """
            screen_image = self.browser.get_screenshot_as_png()
            screenshot = Image.open(BytesIO(screen_image))
            # screenshot.save("screenshot.png")
            return screenshot
    
        def image_png(self):
            """
            通过获取网页截图,然后进行切片,返回四张图片
            :return:
            """
            screenshot = self.save_screen_png()
            images = []
            for num in range(1, 5):
                # 依次获取4张图片,存入iamges列表中
                images.append(self.get_image(screenshot, num))
            return images
    
        def get_image(self, screenshot, number):
            """
            获取四张图片的下标
            :return: left, top, right, bottom
            """
            image = self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='Rotate-background'][{}]".format(number))))
            location = image.location
            size = image.size
            top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
                'width']
            image = screenshot.crop((left, top, right, bottom))
            # image.save("image{}.png".format(number))
            return image
    
        def image_turn_num(self, image):
            """
            用获取的图片跟图片库的图片比较,
            :param image: 原图
            :return:
            """
            for i in range(0, 4):
                # 原图最多转三次
                dir_path = "./static/images/"
                change_image = image.rotate(-90*i)
                # change_image.save("change{}.png".format(i))
                for or_path in os.listdir(dir_path):
                    or_image = Image.open(os.path.join(dir_path, or_path))
                    result = self.examine_pixel(or_image, change_image)
                    print("result:%s",result)
                    print("i:%s", i)
                    if result:
                        return i
            return 5
    
        def examine_pixel(self, image1, image2):
            """
            判断来个图片是否相等
            :param image1: 图片1
            :param image2: 图片2
            :return:
            """
            thredhold = 100
            for x in range(image1.size[0]):
                for y in range(image1.size[1]):
                    pixel1 = image1.load()[x, y]
                    pixel2 = image2.load()[x, y]
                    if not (abs(pixel1[0] - pixel2[0]) < thredhold and abs(pixel1[1] - pixel2[1]) < thredhold and abs(pixel1[2] - pixel2[2]) < thredhold):
                        return False
            return True
    
        def click_submit(self):
            """
            点击登录按钮
            :return: None
            """
            submit = self.wait.until(EC.element_to_be_clickable((By.ID, "submitLoginBtn")))
            submit.click()
    
        def __del__(self):
            self.browser.quit()
    
    
    def main():
        """pass"""
        c = Crack()
        c.login()
    
    
    if __name__ == "__main__":
        main()
    
    

    至此实现了登录操作。
    我在采集样本时采了10000组验证码,一组验证码切割成4张图片,总计40000张样本,去重处理后剩下400多张验证码图片,但是在登陆匹配时很少有能够匹配的时候,原因在于验证码的链接是php网页,会随机调取图片,但是我取了共40000张,去重后只有400张,而且登陆时还一直无法匹配上,所以我推测大概类似于这个设计,将验证码装在很多个类里面,每个类之间是平行的关系,每次我调取一个类,然后从里面随机取调取图片,所以也就是造成了我采了如此多的样本确一直是如此低的匹配率的原因,当然这只是个人推测。如果要实现获取全部的样本也不无可能,但是一是采集样本的时间,二是匹配时也会浪费非常多的时间。
    所以我最终采用的方案是利用第三方登陆绕过图片验证。
    首先先进入登陆界面(并非真正的登陆界面,如下)
    在这里插入图片描述
    我们需要点击图中圈出来的登陆按钮,看下网页源代码(通过Ctrl+shift+C选定顶元素)
    在这里插入图片描述
    1实现点击 “登陆” 按钮

    self.browser.find_element_by_class_name("login_link").click()  # 点击"登陆按钮",打开登陆界面
    

    2实现点击 “知道了”按钮
    在这里插入图片描述
    实现代码:

    self.browser.find_element_by_xpath('//*[@id="J_loginMaskClose"]').click()  # 点击知道了按钮
    

    3点击 “QQ登陆”按钮
    在这里插入图片描述
    实现代码如下:

    self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'qq'))).click()  
    

    点击后会弹出qq登陆弹窗,这是一个独立于原来窗口得新窗口
    在这里插入图片描述
    新窗口默认登陆方式为扫码登陆,或者当前电脑登陆qq时可点击头像登陆,但是我们要实现自动填写账号密码的,账号密码登陆。
    这里就涉及到一个新的问题,如何去定位新弹出窗口的元素,按照原来的方式一定是无法定位的。
    这里通过窗口句柄来实现窗口间的切换,而且在切换时也要做iframe的切换,然后只需要通过上文所讲的方法去定位并点击账号密码登陆按钮,
    在这里插入图片描述
    4填入账号和密码并点击登陆
    在这里插入图片描述
    在这里插入图片描述
    实现代码:

    self.browser.switch_to.window(i)#切换到新打开的窗口
    self.browser.switch_to.frame("ptlogin_iframe")#切换打新窗口的iframe
                    WebDriverWait(self.browser,20).until(EC.element_to_be_clickable((By.ID, 'switcher_plogin'))).click()
    name_page = self.browser.find_element_by_name('u')
    name_page.send_keys(usr)
    password_page = self.browser.find_element_by_name('p')
    password_page.send_keys(pas)
    self.click_submit()
    

    5切换回主窗口,然后关闭
    实现代码:

    time.sleep(0.5)
    self.browser.switch_to.window(first_handle)  # 切换到主的窗口
    self.browser.switch_to.default_content()#返回默认iframe
    self.browser.quit()#关闭浏览器
    

    以下是全部代码:

    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    import time
    import requests
    import pandas
    from lxml import etree
    from fake_useragent import UserAgent
    import re
    import numpy
    
    
    #登陆当当网
    class Crack(object):
        def __init__(self):
            self.login_url = "http://www.dangdang.com/"
            self.chrome_options = webdriver.ChromeOptions()
            self.chrome_options.add_argument("service_args=['–ignore-ssl-errors=true', '–ssl-protocol=TLSv1']")
            self.browser = webdriver.Chrome()
            #self.browser.minimize_window()#最小化
            self.browser.maximize_window()# 最大化
            self.size_flag=True#控制登陆弹出窗口显示 True最小化,False最小化
            self.wait = WebDriverWait(self.browser, 4)
            time.sleep(0.5)
    
        def login(self,key,usr,pas):
            """
            输入账号,密码
            :return:None
            在此函数里完成全部操作,因为浏览器退出后
            """
            #try:
            self.browser.get(self.login_url)
            first_handle = self.browser.current_window_handle#在这里得到当前窗口句柄
            self.browser.find_element_by_class_name("login_link").click()  # 点击"登陆按钮",打开登陆界面
            self.browser.find_element_by_xpath('//*[@id="J_loginMaskClose"]').click()  # 点击知道了按钮
            self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'qq'))).click()  #
            #browser.find_element_by_class_name('qq').click()  # 点击"qq登陆按钮",打开qq登陆界面
            handles = self.browser.window_handles#获取所有窗口句柄
            for i in handles:#在所有窗口中查找弹出窗口
                if i != first_handle:
                    self.browser.switch_to.window(i)#切换到新打开的窗口
                    self.browser.switch_to.frame("ptlogin_iframe")#切换打新窗口的iframe
                    WebDriverWait(self.browser,20).until(EC.element_to_be_clickable((By.ID, 'switcher_plogin'))).click()
                    name_page = self.browser.find_element_by_name('u')
                    name_page.send_keys(usr)
                    password_page = self.browser.find_element_by_name('p')
                    password_page.send_keys(pas)
                    self.click_submit()
                    time.sleep(0.5)
                    self.browser.switch_to.window(first_handle)  # 切换到主的窗口
                    self.browser.switch_to.default_content()#返回默认iframe
                    self.browser.quit()#关闭浏览器
                else:
                    pass
    
        def click_submit(self):
            """
            点击登录按钮
            :return: None
            """
            submit = self.wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="login_button"]')))
            submit.click()
    if __name__ == "__main__":
        #usr="此处填自己的QQ号"
        #pas = "此处填自己的QQ号密码"
        usr='你的QQ号'
        pas='你的QQ号密码'
        c = Crack()
        c.login('python',usr,pas)
    
    
    展开全文
  • python 爬虫 爬取当当网图书信息

    千次阅读 2018-04-14 08:56:29
    初次系统的学习python,在学习完基本语法后,对爬虫进行学习,现在对当当网进行爬取,爬取了基本图书信息,包括图书名、作者等import requests from time import sleep from lxml import etree class dangdang_...
    初次系统的学习python,在学习完基本语法后,对爬虫进行学习,现在对当当网进行爬取,爬取了基本图书信息,包括图书名、作者等
    import requests
    from time import sleep
    from lxml import etree
    class dangdang_spider():
    #定义爬虫类
        def __init__(self):
            self.url="http://category.dangdang.com/pg{}-cp01.00.00.00.00.00.html" #爬虫网址
            self.headers= {#设置headers
                'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
    #爬取基本网页信息
        def parse(self,url):
             r=requests.get(url,headers=self.headers)
             return r.content.decode(encoding='gbk')
    #对数据处理
        def handle_data(self, data,i):
            html = etree.HTML(data)#对信息进行html格式化
            msg_list=[]
            li_list=html.xpath("// ul[ @ id = 'component_0__0__6612']/li")#利用xpath锁定图书信息所在的html标签
            for li in li_list:
                msg = {}
                msg['book_title'] = li.xpath('./p/a/text()')[0]
                msg['book-author'] = li.xpath('./p/span[1]/a[1]/@title')[0]if len(li.xpath('./p/span[1]/a[1]/@title')) >0 else '无'
                msg['book-publish'] = li.xpath('./p/span[3]/a/text()')[0]if len(li.xpath('./p/span[3]/a/text()')) >0 else '无'
                msg['book-publish_time'] = li.xpath('./p[5]/span[2]/text()')[0].replace(' /','')if len(li.xpath('./p[5]/span[2]/text()')) >0 else '无'
                msg['book-descrip'] = li.xpath('./p[2]/text()')[0]if len(li.xpath('./p[2]/text()')) >0 else '无'
                msg['book-price'] = li.xpath('./p[3]/span[1]/text()')[0]
                msg['book-pinglun'] = li.xpath('./p[4]/a/text()')[0]
                msg_list.append(msg)
            # print(msg_list)
    
            next_url = self.url.format(i) #构建下一页url
            return msg_list, next_url
        def save_data(self,data):
            for msg in data:
                msg_str=msg['book_title']+','+msg['book-author']+','+msg['book-publish']+','+msg['book-publish_time']+','+msg['book-descrip']+','+msg['book-price']+','+msg['book-pinglun']
                print(msg_str)
                with open('dangdang.csv','a',encoding='utf-8') as f: #写入文件
                    f.write(msg_str)
                    f.write('\n')
        def run(self):
            i=1
            next_url=self.url.format(i)
            while next_url:
                html_str=self.parse(next_url)
                i = i + 1
                msg_list, next_url=self.handle_data(html_str,i)
                self.save_data(msg_list)
                print(next_url)
                sleep(2)
    
    if __name__ == '__main__':
        d=dangdang_spider()
        d.run()
    

    爬取结果截图 爬取结果截图
    展开全文
  • Python爬虫系列之当当网小爬虫与简单的数据可视化分析.pdf
  • python爬虫爬取当当网的商品信息

    千次阅读 2019-11-28 23:08:58
    python爬虫爬取当当网的商品信息一、环境搭建二、简介三、当当网网页分析1、分析网页的url规律2、解析网页html页面书籍商品html页面解析其他商品html页面解析四、代码实现 一、环境搭建 使用到的环境: python3.8.0...

    一、环境搭建

    使用到的环境:

    • python3.8.0
    • requests库
    • re库
    • bs4库
    • pycharm

    二、简介

    代码实现了根据设定的关键字keyword获取相关商品的资源定位符(url),然后批量爬取相关页面的商品信息,另外之所以选择当当网是因为当当网的网页商品信息不是动态加载的,因此可以直接爬取获得,例如京东、拼多多的网页就是动态加载的,博主暂时还不会解析动态加载的页面😅😅😅

    三、当当网网页分析

    1、分析网页的url规律

    首先是分析出当当网的搜索商品的url,浏览器进入当当网主页,在检索栏输入任意的商品关键词,可以看到打开的页面的url链接形式为如下形式:

    url = 'http://search.dangdang.com/?key={}&act=input'.format(keyword)     
    

    然后获取页面翻页可以发现这类商品的每一页的url形式为:

    url = "http://search.dangdang.com/?key={}&input&page_index={}".format(keyword,page_count)     
    

    分析出了这些规律之后,就可以根据输入的关键词自动生成相应页面的url,然后像服务器发送请求,得到每页的html信息。

    2、解析网页html页面

    浏览器打开任一网页,打开网页调试工具可以发现,当当网的商品网页分为两类(很奇葩),第一类是书籍类商品的网页,第二类是其他商品。

    书籍商品html页面解析

    经过调试发现,页面的商品信息都在ul标签下的中li标签中,每个li标签块存放一个商品的所有信息,商品的一些信息例如namepriceauthor等信息又存放在li标签下对应的p标签中,因此解析每个p标签的相关属性信息,就能获得对应的信息。值得一提的是书籍商品页面的ul标签的class属性是"bigmig"。

    其他商品html页面解析

    其他商品和书籍类商品的商品信息存放的标签块基本相同,唯一不同的是其ul标签的class属性是"bigimg cloth_shoplist",解析类似,只不过要区别得到的ul标签的class属性。

    四、代码实现

    代码主要包括四个函数:

    def getMaxPageCount(keyword):
        # 爬取商品的最大页面数
        try:
            user_agent = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'}
            max_page_count = 0
            url = 'http://search.dangdang.com/?key={}&act=input'.format(keyword)
            rspon = requests.get(url, headers = user_agent)
            rspon.encoding = rspon.apparent_encoding
            rspon.raise_for_status()
        except:
            print("爬取页数失败:", rspon.status_code)
            return max_page_count
        html = BeautifulSoup(rspon.text, "html.parser")
        ul_tag = html.find('ul', {"name":"Fy"})
        for child in ul_tag.children:
            if type(child) == type(ul_tag):
                match = re.match(r"\d+",str(child.string))
                if match:
                    temp_num = int(match.group(0))
                    if temp_num > max_page_count:
                        max_page_count = temp_num
        return max_page_count
    

    getMaxPageCount(keyword) 函数主要是根据输入的商品种类关键词爬取相应的网页,获取当前种类商品的最大页数。

    def getOnePageMsg(product_list, url):
        # 爬取一个页面的商品数据
        try:
            user_agent = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'}
            rspon = requests.get(url, headers = user_agent)
            rspon.encoding = rspon.apparent_encoding
            rspon.raise_for_status()
        except:
            print("爬取页数失败:", rspon.status_code)
        html = BeautifulSoup(rspon.text, "html.parser")
        ul_tag = html.find('ul', {"class": "bigimg cloth_shoplist"})
        if ul_tag:
            search_type = 2
        else:
            ul_tag = html.find('ul', {"class": "bigimg"})
            if ul_tag:
                search_type = 1
            else:
                return
        if search_type ==1:
            for child in ul_tag.children:
                if type(child) == type(ul_tag):
                    temp_list = []
                    # 保存书名
                    tag_name = (child.find('p',{"class":"name"})).find('a')
                    temp_list.append(str(tag_name.attrs["title"]))
                    # 保存价格
                    tag_price = (child.find('p',{"class":"price"})).find("span", {"class":"search_now_price"})
                    temp_list.append(str(tag_price.string))
                    # 保存作者
                    tag_author = child.find('p', {"class":"search_book_author"}).find('a',{"name":"itemlist-author"})
                    if tag_author:
                        temp_list.append(str(tag_author.string))
                    else:
                        temp_list.append(str("NULL"))
                    # 保存出版社
                    tag_pub = child.find('p', {"class": "search_book_author"}).find('a', {"name": "P_cbs"})
                    if tag_pub:
                        temp_list.append(str(tag_pub.string))
                    else:
                        temp_list.append(str("NULL"))
                    #保存评价
                    tag_comment = child.find('p',{"class":"search_star_line"}).find('a')
                    temp_list.append(str(tag_comment.string))
                    product_list.append(temp_list)
        else:
            for child in ul_tag.children:
                if type(child) == type(ul_tag):
                    temp_list = []
                    # 保存商品名
                    tag_name = (child.find('p',{"class":"name"})).find('a')
                    temp_list.append(str(tag_name.attrs["title"]))
                    # 保存价格
                    tag_price = (child.find('p',{"class":"price"})).find("span")
                    temp_list.append(str(tag_price.string))
                    #保存评价
                    tag_comment = child.find('p',{"class":"star"}).find('a')
                    temp_list.append(str(tag_comment.string))
                    product_list.append(temp_list)
    

    getOnePageMsg(product_list, url) 函数实现的是根据一个url爬取网页的html信息,然后解析信息得到需要的商品信息,主要是使用bs4库的BeautifulSoup解析网页结构,并且将数据存入product_list列表中。

    def getAllPageMsg(keyword, product_list):
        # 爬取商品的所有页面的数据
        page_count = getMaxPageCount(keyword)
        print("find ",page_count," pages...")
        for i in range(1,page_count+1):
            url = "http://search.dangdang.com/?key={}&input&page_index={}".format(keyword,i)
            getOnePageMsg(product_list,url)
            print(">> page",i," import successfully...")
    

    getAllPageMsg(keyword, product_list) 函数实现的是根据关键词首先获取这一商品的最大页面,然后自动化生成每一个页面的url,在调用getOnePageMsg函数解析每个url页面,得到数据列表product_list。

    def writeMsgToFile(path,keyword,product_list):
        # 将爬取的数据保存到文件
        file_name = "{}.txt".format(keyword)
        file = open(path+file_name, "w", encoding='utf-8')
        for i in product_list:
            for j in i:
                file.write(j)
                file.write(' ')
            file.write("\n")
        file.close()
    

    writeMsgToFile(path,keyword,product_list) 函数主要实现将列表的数据写入文件,当然还有根据传入的路径和关键字生成对应的文件名。

    def main():
        # 函数入口
        keyword = 'python'
        path="E://"
        product_list = []
        getAllPageMsg(keyword,product_list)
        writeMsgToFile(path,keyword,product_list)
    

    最后main()函数是实现方法的入口,包括一些参数的初始化设置。
    执行main()函数就能实现网页数据的爬取,以下是以python为关键词,爬取页面得到的结果,抓取解析完所有页面花了3分钟左右。
    在这里插入图片描述
    在这里插入图片描述

    可以看到最终生成的txt文件一共有6000行,每一行保存的就是商品的信息。

    展开全文
  • # @data 2019/12/4 15:02 import requests from bs4 import BeautifulSoup import random import bs4 my_headers = [ "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0...
  • 利用Python爬取并简单地可视化分析当当网的图书数据。 让我们愉快地开始吧~ 开发工具 Python版本:3.6.4 相关模块: requests模块; bs4模块; wordcloud模块; jieba模块; pillow模块; pyecharts模块; 以及一些...
  • 目标站点需求分析 ...获取当当网每个图书名字和评论数 涉及的库 scrapy,mysql 获取解析单页源码 保存到数据库中 结果 转载于:https://www.cnblogs.com/du-jun/p/10403326.html...
  • 利用Python爬取并简单地可视化分析当当网的图书数据。开发工具Python版本:3.6.4相关模块:requests模块;bs4模块;wordcloud模块;jieba模块;pillow模块;pyecharts模块;以及一些Python自带的模块。环境搭建安装...
  • Python爬虫深入 爬取当当网商品基本信息 使用scrapy爬虫框架,创建爬虫项目。 基本命令: scrapy startproject dangdang scrapy genspider -l scrapy genspider -t basic dd dangdang.com 在dangdang这个爬虫项目下...
  • 【实例简介】python爬虫爬取当当网【实例截图】【核心代码】'''Function:当当网图书爬虫Author:Charles微信公众号:Charles的皮卡丘'''import timeimport pickleimport randomimport requestsfrom bs4 import ...
  • 来啦,老弟我们已经知道怎么...所以我们爬取当当网的前 500本好五星评书籍怎么样?ok接下来就是请在电脑的陪同下边看本文边练习首先我们要对我们的目标网站进行分析先摸清对方的底我们才能战无不胜打开这个书籍排行...
  • 一、爬虫入门网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。运用python3.6中的urllib.request1.快速爬取一个...
  • 本实例还有另外的离线爬虫实现,有兴趣可点击离线爬虫爬取当当网2018年10月畅销Top500的图书信息 爬虫说明 1.使用requests和Lxml库爬取,(用BS4也很简单,这里是为了练习Xpath的语法) 2.爬虫分类为两种,一种是...
  • 利用python爬虫可视化分析当当网的图书数据!.pdf

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,267
精华内容 506
关键字:

python爬虫爬当当网

python 订阅
爬虫 订阅