1、网站分析
1.1 加载的首页
上图是首页源码的元素分析,如果能拿到这个网页源码,包含了大类小说和小类中国当代小说等的链接,然后再放到解析函数去解析即可。但是这个页面源码通过request请求是获取不到的。
找到大类小类的数据也是通过JSON动态加载的。
这里面的数据并没有相关的详情链接,因此没有办法使用。这里通过scrapy爬虫中间件技术来使用selenium获取pagesource,这个页面源代码就是和第一张图片内的elements数据一样了。
1.2 我们需要从大类链接中再次遍历小类链接,获得小类链接的解析,从详情页中提取数据,详情页还需要翻页。大类大概有50多个,每个小类大约10个,小类大约100页,每页60本图书介绍,这样如果都爬完,可以获得5010100*60=3000000条信息,我这只是学习用,因此,这边放了个切片,比如:for big_node in big_node_list[0:1]:这个后面的[0:1]只是获取第一个大类节点的链接,如果把切片去掉,就是获取全部大类的链接。
2 selenium
Selenium是一个用于Web应用程序测试的工具,用Python语言可以自动化操作浏览器,模拟用户的行为,但是效率确实相对接口来说有些低,但是我们这里只需要运行一次拿到全部链接即可。根据浏览器的不同和不同的版本,下载相应的驱动。我用的是谷歌,CNPM Binaries Mirror
3 中间件文件内容
from scrapy import signals
import time
from selenium import webdriver
# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter
from scrapy.http import HtmlResponse
class SelenimuMiddleWare(object):
def process_request(self,request,spider):
url =request.url
#print(url)
options = webdriver.ChromeOptions()
options.add_experimental_option('useAutomationExtension', False)
options.add_experimental_option('excludeSwitches', ['enable-automation'])
options.add_argument('--headless')#无头浏览器
options.add_argument('--disable-gpu')
driver = webdriver.Chrome(options=options)
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{"source":"""
Object.defineProperty(navigator,'webdriver',{
get: () => undefined
})
"""})
driver.get(url)
time.sleep(5)
data = driver.page_source
driver.close()
res = HtmlResponse(url=url,body = data,encoding='utf-8',request = request)
return res
(1)、options是启动浏览器的启动参数,1-2行,是消除浏览器上受自动化测试软件的控制,3-4行是无头模式,有时不想让过程显示出来,driver.execute_cdp_cmd是把浏览器一个属性为webdriver取消定义,这样不让服务器发现是自动化软件控制,不然不给你返回内容。
(2)、这些都是固定格式,可以封装为其他函数放到外面,在中间件里引用也是可以的。
4、爬虫页面文件
import scrapy
from jdbooks.items import JdbooksItem
#导入分布式爬虫类,
#redis-server.exe redis.windows.conf
#scrapy runspider jd.py
#lpush myjd https://book.jd.com/booksort.html
from scrapy_redis.spiders import RedisCrawlSpider
next_page = 1
i = 0
class JdSpider(scrapy.Spider):
name = "jd"
#设置redis_key
# redis_key = 'myjd'
allowed_domains = ["jd.com"]
start_urls = ["https://book.jd.com/booksort.html"]
def __init__(self):
# domain = kwargs.pop('domain','')
# self.allowed_domains = list(filter(None,domain.split(',')))
# super(JdSpider,self).__init__(*args,**kwargs)
self.table_name = 'jdbooks'
self.table_fields = ['big_category','big_category_link','small_category','small_category_link',
'book_point','book_name','author','link','press','pub_date','descinfo','price']
def parse(self, response):
big_node_list =response.xpath('//*[@id="booksort"]/div[2]/dl/dt/a')
print(len(big_node_list))
for big_node in big_node_list[0:1]:
big_category = big_node.xpath('./text()').extract_first()
big_category_link = response.urljoin(big_node.xpath('./@href').extract_first())
small_node_list = big_node.xpath('../following-sibling::dd[1]/em/a')
item = JdbooksItem()
item['table_fields'] = self.table_fields
item['table_name'] = self.table_name
for small_node in small_node_list[0:1]:
item['big_category'] = big_category
item['big_category_link'] = big_category_link
item['small_category'] = small_node.xpath('./text()').extract_first()
item['small_category_link'] = response.urljoin(small_node.xpath('./@href').extract_first())
#模拟点击小分类链接到详情页再去取数据
yield scrapy.Request(
url = item['small_category_link'],
callback = self.parse_detail,
meta={'item':item}
)
def parse_detail(self, response):
item = response.meta['item']
book_lists = response.xpath('//*[@id="J_goodsList"]/ul/li/div')
for book_list in book_lists:
item['book_point'] = book_list.xpath('./div[3]/a/em/text()').extract_first().strip()
string = str(item['book_point'])
if '】' in string:
item['book_name'] = string.split('】')[1].split('(')[0].split(' ')[0]
else:
item['book_name'] = string.split('(')[0].split(' ')[0]
item['author'] = book_list.xpath('./div[4]/span[1]/a/text()').extract_first()
item['link'] = response.urljoin(book_list.xpath('./div[3]/a/@href').extract_first().strip())
item['press'] = book_list.xpath('./div[4]/span[2]/a/text()').extract_first()
item['pub_date'] = book_list.xpath('./div[4]/span[3]/text()').extract_first()
item['descinfo'] = book_list.xpath('./div[3]/a/@title').extract_first().strip()
item['price'] = book_list.xpath('./div[2]/strong/i/text()').extract_first()
#print(item)
yield item
# #模拟翻页
flag = response.xpath('//*[@id="J_bottomPage"]/span[1]/a[last()]/@href').extract_first()
print('================================================================')
# https://list.jd.com/list.html?cat=1713%2C3258%2C3297&page=3&s=57&click=0
# https://list.jd.com/list.html?cat=1713%2C3258%2C3297&page=5&s=117&click=0
# https://list.jd.com/list.html?cat=1713%2C3258%2C3297&page=7&s=177&click=0
# https://list.jd.com/list.html?cat=1713%2C3258%2C3297&page=9&s=237&click=0
global next_page , i
next_page = next_page + 2
i= i+1
if flag != '':
num = 60*i -3
cat = item['small_category_link'].split('.com/')[1].split('.')[0].replace('-','%2C')
next_url = 'https://list.jd.com/list.html?cat={}&page={}&s={}&click=0'.format(cat,next_page,num)
print(next_url)
yield scrapy.Request(
url=next_url,
callback=self.parse_detail,
meta={'item':item}
)
模拟翻页的时候注意链接的变化规律,一共有3个变量需要传递,其中cat是小说分类里面的小类,从item里面获得,需要处理下。还有2个页面的数字,注意下变化的规律即可。
5、数据写入Mysql即可。
本案例仅供学习使用。
版权归原作者 u010152658 所有, 如有侵权,请联系我们删除。