整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

爬虫实战系列1:爬取当当网所有数据分析书籍信息

爬虫实战系列1:爬取当当网所有数据分析书籍信息

天我们用最常用也最方便的Lxml库以及对应的Xpath语法来进行实践学习,爬取当当网数据分析书籍信息。


1、爬取目标


对于要爬取的当当网书籍信息,首先打开当当网页,以数据分析为关键字搜索出来页面所有书籍信息。如下图:



本次爬取的结果有11项:

(1)每页序号

(2)商品ID

(3)标题

(4)书籍价格

(5)书籍原价

(6)书籍折扣

(7)电子书价格

(8)作者

(9)出版时间

(10)出版社

(11)书籍评论量


2、爬取过程


(1)确定url地址


分析网页页面,当输入数据关系关键字以后,点击搜索出来所有书籍网页信息后,将网页拉到最下面可以看到如下图:


可见此网页是翻页的,因此,点击页面2、3、1,提取网页的url如下:


http://search.dangdang.com/?key=%CA%FD%BE%DD%B7%D6%CE%F6&act=input&page_index=2


http://search.dangdang.com/?key=%CA%FD%BE%DD%B7%D6%CE%F6&act=input&page_index=3


http://search.dangdang.com/?key=%CA%FD%BE%DD%B7%D6%CE%F6&act=input&page_index=1


从每页的URL地址信息可以发现每一个页面的地址差异就是page_index的值,所以url地址可以确认为:


http://search.dangdang.com/?key=%CA%FD%BE%DD%B7%D6%CE%F6&act=input&page_index=


page_index的值,我们可以通过循环依次在地址后面添加。


代码如下:

urls = ['http://search.dangdang.com/?key=%CA%FD%BE%DD%B7%D6%CE%F6&act=input&page_index={}'.format(i) for i in range(1,101)]


(2)确定爬取节点


有了url地址,就可以使用lxml库来获取网页信息进行解析,得到具体的爬取信息。鼠标右键,点击“检查”,如下图:



通过网页html里面的信息对应查找,可以找到li对应每个书籍信息,详细的信息如下图可见:



可以看到书籍名字、价格等信息,然后通过Xpath可以逐一提取出来。详细代码如下:


html=requests.get(url,headers=headers)
# html.encoding="utf-8"
# print('第一层调用是否返回正常:',html)
html.encoding=html.apparent_encoding  # 将乱码进行编码
selector=etree.HTML(html.text)
# print(selector)
datas=selector.xpath('//div[@class="con shoplist"]')
# print(datas)

for data in datas:
   Classs=data.xpath('div/ul/li/@class')                        #line1-line60
   IDDs=data.xpath('div/ul/li/@id')                              #id
   titles=data.xpath('div/ul/li/a/@title')                      #标题
   prices=data.xpath('div/ul/li/p[3]/span[1]/text()')           #书籍价格
   source_prices=data.xpath('div/ul/li/p[3]/span[2]/text()')    #书籍原价
   discounts=data.xpath('div/ul/li/p[3]/span[3]/text()')        #书籍折扣
   # dian_prices=data.xpath('div/ul/li/p[3]/a[2]/i/text()')       #电子书价格
   authors=data.xpath('div/ul/li/p[5]/span[1]/a[1]/@title')     #作者
   publish_times=data.xpath('div/ul/li/p[5]/span[2]/text()')    #出版时间
   publishs=data.xpath('div/ul/li/p[5]/span[3]/a/text()')       #出版社
   comments=data.xpath('div/ul/li/p[4]/a/text()')               #书籍评论量
   urls=data.xpath('div/ul/li/a/@href')


备注:基于如果想爬取电子书价格,由于有些书籍没有电子书价格,因此爬取出来会错行,务必通过提取书籍页面url,递归再次爬取详情页进行空值处理,避免错行。


(3)保存爬取信息入数据库


此处我们将爬取的信息存储入数据库,就需要先连接数据库和建立数据库表,便于后续存储。数据连接和表建立代码如下:


db=pymysql.connect(host='localhost', user='root', passwd='库密码', db='库名称:Learn_data', port=3306, charset='utf8')
print("数据库连接")
cursor=db.cursor()
cursor.execute("DROP TABLE IF EXISTS Learn_data.dangdangweb_info_detail")
sql="""CREATE TABLE IF not EXISTS Learn_data.dangdangweb_info_detail (
   id int auto_increment primary key,
   Class CHAR(100),
   IDD CHAR(100),
   title CHAR(100),
   price CHAR(100),
   source_price CHAR(100),
   discount CHAR(100),
   author CHAR(100),
   publish_time CHAR(100),
   publish CHAR(100),
   comment CHAR(100),
   dian_price CHAR(100)
)
DEFAULT CHARSET=utf8"""
cursor.execute(sql)


爬取的数据存储入表中代码如下:

cursor.execute("insert into dangdangweb_info_detail (Class,IDD,title,price,source_price,discount,author,publish_time,publish,comment,dian_price)"
              "values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
       (str(Class),str(IDD),str(title),str(price),str(source_price),str(discount),str(author)
        ,str(publish_time),str(publish),str(comment),str(dian_price[0])))


最后必须使用:db.commit() 关闭数据库,不然数据无法正确存储入表。


3、爬取结果


最后,我们将上面的代码整合起来就可以正常爬取。存储的结果截图如下:



4、写在最后


这次实战演练就算是结束了, 后续就是使用数据进行分析了。总结一下程序不足的地方有如下几点:

(1)该程序是单线程,没有使用多线程,在执行的效率上还有待提高

(2)对于页面的字段信息可能存在错行,对程序的兼容性需要优化

(3)没有使用随机User-Agent和代理,容易被封IP


--END--

篇文章主要介绍了Python爬取当当、京东、亚马逊图书信息代码实例,具有一定借鉴价值,需要的朋友可以参考下。

注:

1.本程序采用MSSQLserver数据库存储,请运行程序前手动修改程序开头处的数据库链接信息

2.需要bs4、requests、pymssql库支持

3.支持多线程

from bs4 import BeautifulSoup 
?
import re,requests,pymysql,threading,os,traceback 
?
 
?
try: 
?
 conn=pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='root', db='book',charset="utf8") 
?
 cursor=conn.cursor() 
?
except: 
?
 print('\n错误:数据库连接失败') 
?
 
?
#返回指定页面的html信息 
?
def getHTMLText(url): 
?
 try: 
?
 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'} 
?
 r=requests.get(url,headers=headers) 
?
 r.raise_for_status() 
?
 r.encoding=r.apparent_encoding 
?
 return r.text 
?
 except: 
?
 return '' 
?
#返回指定url的Soup对象 
?
def getSoupObject(url): 
?
 try: 
?
 html=getHTMLText(url) 
?
 soup=BeautifulSoup(html,'html.parser') 
?
 return soup 
?
 except: 
?
 return '' 
?
#获取该关键字在图书网站上的总页数 
?
def getPageLength(webSiteName,url): 
?
 try: 
?
 soup=getSoupObject(url) 
?
 if webSiteName=='DangDang': 
?
 a=soup('a',{'name':'bottom-page-turn'}) 
?
 return a[-1].string 
?
 elif webSiteName=='Amazon': 
?
 a=soup('span',{'class':'pagnDisabled'}) 
?
 return a[-1].string 
?
 except: 
?
 print('\n错误:获取{}总页数时出错...'.format(webSiteName)) 
?
 return -1
?
 
?
class DangDangThread(threading.Thread): 
?
 def __init__(self,keyword): 
?
 threading.Thread.__init__(self) 
?
 self.keyword=keyword 
?
 def run(self): 
?
 print('\n提示:开始爬取当当网数据...') 
?
 count=1
?
 
?
 length=getPageLength('DangDang','http://search.dangdang.com/?key={}'.format(self.keyword))#总页数 
?
 tableName='db_{}_dangdang'.format(self.keyword) 
?
 
?
 try: 
?
 print('\n提示:正在创建DangDang表...') 
?
 cursor.execute('create table {} (id int ,title text,prNow text,prPre text,link text)'.format(tableName)) 
?
 print('\n提示:开始爬取当当网页面...') 
?
 for i in range(1,int(length)): 
?
 url='http://search.dangdang.com/?key={}&page_index={}'.format(self.keyword,i) 
?
 soup=getSoupObject(url) 
?
 lis=soup('li',{'class':re.compile(r'line'),'id':re.compile(r'p')}) 
?
 for li in lis: 
?
 a=li.find_all('a',{'name':'itemlist-title','dd_name':'单品标题'}) 
?
 pn=li.find_all('span',{'class': 'search_now_price'}) 
?
 pp=li.find_all('span',{'class': 'search_pre_price'}) 
?
 
?
 if not len(a)==0: 
?
 link=a[0].attrs['href'] 
?
 title=a[0].attrs['title'].strip() 
?
 else: 
?
 link='NULL'
?
 title='NULL'
?
 
?
 if not len(pn)==0: 
?
 prNow=pn[0].string 
?
 else: 
?
 prNow='NULL'
?
 
?
 if not len(pp)==0: 
?
 prPre=pp[0].string 
?
 else: 
?
 prPre='NULL'
?
 sql="insert into {} (id,title,prNow,prPre,link) values ({},'{}','{}','{}','{}')".format(tableName,count,title,prNow,prPre,link) 
?
 cursor.execute(sql) 
?
 print('\r提示:正在存入当当数据,当前处理id:{}'.format(count),end='') 
?
 count +=1
?
 conn.commit() 
?
 except: 
?
 pass
?
 
?
class AmazonThread(threading.Thread): 
?
 def __init__(self,keyword): 
?
 threading.Thread.__init__(self) 
?
 self.keyword=keyword 
?
 
?
 def run(self): 
?
 print('\n提示:开始爬取亚马逊数据...') 
?
 count=1
?
 length=getPageLength('Amazon','https://www.amazon.cn/s/keywords={}'.format(self.keyword))#总页数 
?
 tableName='db_{}_amazon'.format(self.keyword) 
?
 
?
 try: 
?
 print('\n提示:正在创建Amazon表...') 
?
 cursor.execute('create table {} (id int ,title text,prNow text,link text)'.format(tableName)) 
?
 
?
 print('\n提示:开始爬取亚马逊页面...') 
?
 for i in range(1,int(length)): 
?
 url='https://www.amazon.cn/s/keywords={}&page={}'.format(self.keyword,i) 
?
 soup=getSoupObject(url) 
?
 lis=soup('li',{'id':re.compile(r'result_')}) 
?
 for li in lis: 
?
 a=li.find_all('a',{'class':'a-link-normal s-access-detail-page a-text-normal'}) 
?
 pn=li.find_all('span',{'class': 'a-size-base a-color-price s-price a-text-bold'}) 
?
 if not len(a)==0: 
?
 link=a[0].attrs['href'] 
?
 title=a[0].attrs['title'].strip() 
?
 else: 
?
 link='NULL'
?
 title='NULL'
?
 
?
 if not len(pn)==0: 
?
 prNow=pn[0].string 
?
 else: 
?
 prNow='NULL'
?
 
?
 sql="insert into {} (id,title,prNow,link) values ({},'{}','{}','{}')".format(tableName,count,title,prNow,link) 
?
 cursor.execute(sql) 
?
 print('\r提示:正在存入亚马逊数据,当前处理id:{}'.format(count),end='') 
?
 count +=1
?
 conn.commit() 
?
 except: 
?
 pass
?
 
?
class JDThread(threading.Thread): 
?
 def __init__(self,keyword): 
?
 threading.Thread.__init__(self) 
?
 self.keyword=keyword 
?
 def run(self): 
?
 print('\n提示:开始爬取京东数据...') 
?
 count=1
?
 
?
 tableName='db_{}_jd'.format(self.keyword) 
?
 
?
 try: 
?
 print('\n提示:正在创建JD表...') 
?
 cursor.execute('create table {} (id int,title text,prNow text,link text)'.format(tableName)) 
?
 print('\n提示:开始爬取京东页面...') 
?
 for i in range(1,100): 
?
 url='https://search.jd.com/Search?keyword={}&page={}'.format(self.keyword,i) 
?
 soup=getSoupObject(url) 
?
 lis=soup('li',{'class':'gl-item'}) 
?
 for li in lis: 
?
 a=li.find_all('div',{'class':'p-name'}) 
?
 pn=li.find_all('div',{'class': 'p-price'})[0].find_all('i') 
?
 
?
 if not len(a)==0: 
?
 link='http:' + a[0].find_all('a')[0].attrs['href'] 
?
 title=a[0].find_all('em')[0].get_text() 
?
 else: 
?
 link='NULL'
?
 title='NULL'
?
 
?
 if(len(link) > 128): 
?
 link='TooLong'
?
 
?
 if not len(pn)==0: 
?
 prNow='¥'+ pn[0].string 
?
 else: 
?
 prNow='NULL'
?
 sql="insert into {} (id,title,prNow,link) values ({},'{}','{}','{}')".format(tableName,count,title,prNow,link) 
?
 cursor.execute(sql) 
?
 print('\r提示:正在存入京东网数据,当前处理id:{}'.format(count),end='') 
?
 count +=1
?
 conn.commit() 
?
 except : 
?
 pass
?
def closeDB(): 
?
 global conn,cursor 
?
 conn.close() 
?
 cursor.close() 
?
 
?
def main(): 
?
 print('提示:使用本程序,请手动创建空数据库:Book,并修改本程序开头的数据库连接语句') 
?
 keyword=input("\n提示:请输入要爬取的关键字:") 
?
 
?
 dangdangThread=DangDangThread(keyword) 
?
 amazonThread=AmazonThread(keyword) 
?
 jdThread=JDThread(keyword) 
?
 dangdangThread.start() 
?
 amazonThread.start() 
?
 jdThread.start() 
?
 dangdangThread.join() 
?
 amazonThread.join() 
?
 jdThread.join() 
?
 closeDB() 
?
 print('\n爬取已经结束,即将关闭....') 
?
 os.system('pause') 
?
 
?
main()

示例截图:

关键词:Android下的部分运行结果(以导出至Excel)

.

标:

对xx网上的书籍信息进行提取并保存为txt格式, 标记为(排名,书名,图片地址,作者,推荐指数,五?数量,价格)

步骤:

1、调用requests库,拿到所需要的网页源代码 状态码为200表示返回成功

2、对返回的网页源代码进行解析,使用正则表达式获取想要的关键信息(书名,排名,作者等等......),之后封装数据

3、获取1-25页的1000条书籍信息,使用for循环保存在txt文件里面

关键函数和正则表达式编写实现功能:

自定义函数:

1、parse_dandan(url)

2、parse_result(html)

3、write_item_to_file(item)

4、main(page)

pattern=re.compile('<li>.*?list_num.*?(\d+).</div>.*?<img src="(.*?)".*?class="name".*?title="(.*?)">.*?class="star">.*?class="tuijian">(.*?)</span>.*?class="publisher_info">.*?target="_blank">(.*?)</a>.*?class="biaosheng">.*?<span>(.*?)</span></div>.*?<p><span\sclass="price_n">¥(.*?)</span>.*?</li>',re.S)

代码实现:

'''
对当当网上的书籍信息进行提取
(排名,书名,图片地址,作者,推荐指数,五?数量,价格)

'''''
import requests
import re
import json
'''
请求成功,拿到源代码
'''
def request_dandan(url):
    try:
        resp=requests.get(url)
        if resp.status_code==200:
            return resp.text
    except requests.requestException:
        return None
'''
对其进行解析,使用正则表达式获取
想要的关键信息,之后封装数据
'''

def parse_result(html):
   pattern=re.compile('<li>.*?list_num.*?(\d+).</div>.*?<img src="(.*?)".*?class="name".*?title="(.*?)">.*?class="star">.*?class="tuijian">(.*?)</span>.*?class="publisher_info">.*?target="_blank">(.*?)</a>.*?class="biaosheng">.*?<span>(.*?)</span></div>.*?<p><span\sclass="price_n">¥(.*?)</span>.*?</li>',re.S)
   items=re.findall(pattern,html)
   for item in items:
       yield {
           'range': item[0],
           'iamge': item[1],
           'title': item[2],
           'recommend': item[3],
           'author': item[4],
           'times': item[5],
           'price': item[6]
       }
'''
将获取的数据保存在book.txt里面
'''
def write_item_to_file(item):
    print('开始写入数据====> ' + str(item))
    with open('book.txt', 'a', encoding='UTF-8') as f:
        f.write(json.dumps(item, ensure_ascii=False) + '\n')

        f.close()
'''
定义函数 使用page变量实现翻页功能,requests请求,返回
HTML进行正则解析
'''

def main(page):
    url='http://bang.dangdang.com/books/fivestars/01.00.00.00.00.00-recent30-0-0-1-' + str(page)
    html=request_dandan(url)
    items=parse_result(html)  # 解析过滤我们想要的信息
    for item in items:
        write_item_to_file(item)
'''
获取 1-25页的1000条数据 使用for循环
'''

if __name__=="__main__":
    for i in range(1,26):
        main(i)




运行结果如下:


关注我,让我们一起学习python爬虫,加油