mixmoe / HikariSearch

一款动漫图片搜索引擎聚合网站, 基于 Cloudflare 提供的 Pages Function. / A collection of anime image search engines, based on Cloudflare Pages Function.
https://hikari.obfs.dev
GNU Lesser General Public License v3.0
145 stars 31 forks source link

这个项目能够支持别的serverless服务吗 #7

Closed huankong233 closed 1 year ago

huankong233 commented 1 year ago

比如https://vercel.com/ 因为我想要使用网站的接口,但是被cloudflare拦截了,不知道怎样绕过

mnixry commented 1 year ago

抱歉最近不知道为什么总是收不到通知邮件

目前没有计划, 因为现在的写法和Cloudflare的耦合挺深的, 不是很好处理

如果是请求被CF拦截可以考虑调低防护等级什么的 具体是怎么被拦截的?

huankong233 commented 1 year ago

唔,怎么说呢,反正就是用nodejs的axios去请求cf page里的接口,要么403,要么就是502,但是我部署到本地请求就一点问题都没有了,具体可以看一眼我写的垃圾代码哈哈哈

文件位置:https://github.com/huankong233/kkbot/blob/master/plugins/dove/searchImage/index.js#L133 配置文件:https://github.com/huankong233/kkbot/blob/master/config/pigeon.jsonc#L46

我尝试过了一些别人提供的解决方案,都失败了,使用TLS1.1-1.2版本,并用postman的UA,在第一遍可以正常请求,但是到了第二次就又会被拦截

mnixry commented 1 year ago

请求我demo的接口也会403吗? 其实你bot要是用js写的话可以直接从这里抄代码的

huankong233 commented 1 year ago

请求demo也会,我现在是直接部署到我自己的服务器上了,过段时间有空去看一眼然后抄一下

huankong233 commented 1 year ago

我尝试了一下,好像又被cf拦截了

C:\Users\huan_kong\Desktop\qqbot\bot\plugins\searchImage\libs>node ascii2d.js
Response {
  size: 0,
  [Symbol(Body internals)]: {
    body: PassThrough {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 6,
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: true,
      [Symbol(kCapture)]: false,
      [Symbol(kCallback)]: null
    },
    stream: PassThrough {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 6,
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: true,
      [Symbol(kCapture)]: false,
      [Symbol(kCallback)]: null
    },
    boundary: null,
    disturbed: false,
    error: null
  },
  [Symbol(Response internals)]: {
    type: 'default',
    url: 'https://ascii2d.obfs.dev/search/file',
    status: 500,
    statusText: 'Internal Server Error',
    headers: {
      age: '0',
      'alt-svc': 'h3=":443"; ma=86400, h3-29=":443"; ma=86400',
      'cf-cache-status': 'DYNAMIC',
      'cf-ray': '76e188866bc40439-HKG',
      connection: 'close',
      'content-type': 'text/html; charset=utf-8',
      date: 'Tue, 22 Nov 2022 12:05:27 GMT',
      nel: '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}',
      'report-to': '{"endpoints":[{"url":"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=M7cenqH1Ovqh9y0Y%2Fk8t8MdKRySjblBRaZ2UBkkrOaXWYhSa%2Bx20bq80hMHH1ltz0TU42kfXeTDfBqjcoUE%2Be88%2BBDWfGRR%2B8LkNxdPVcIPNJ3w%2F%2FuuuKeg21249Jh5tlkRR"}],"group":"cf-nel","max_age":604800}',
      server: 'cloudflare',
      'server-timing': 'cf-q-config;dur=7.0000005507609e-06',
      'strict-transport-security': 'max-age=31536000',
      'transfer-encoding': 'chunked',
      vary: 'Accept-Encoding',
      via: '1.1 varnish (Varnish/6.2)',
      'x-content-type-options': 'nosniff',
      'x-request-id': 'c4bf59b5-785c-4e09-9a2c-b9f2e260809c',
      'x-runtime': '0.181728',
      'x-varnish': '429952906'
    },
    counter: 0,
    highWaterMark: 16384
  }
}

这个是代码

import * as cheerio from 'cheerio'

export const BASE_URL = 'https://ascii2d.obfs.dev'

export function parse(body) {
  const $ = cheerio.load(body, { decodeEntities: true })
  return _.map($('.item-box'), item => {
    const detail = $('.detail-box', item),
      hash = $('.hash', item),
      info = $('.info-box > .text-muted', item),
      [image] = $('.image-box > img', item)
    const [source, author] = $('a[rel=noopener]', detail)
    if (!source && !author) return
    return {
      hash: hash.text(),
      info: info.text(),
      image: new URL(image.attribs['src'] ?? image.attribs['data-cfsrc'], BASE_URL).toString(),
      source: source ? { link: source.attribs.href, text: $(source).text() } : undefined,
      author: author ? { link: author.attribs.href, text: $(author).text() } : undefined
    }
  }).filter(v => v !== undefined)
}

import axios from 'axios'
import fetch from 'node-fetch'

async function post(req) {
  const { type, image } = req

  const Form = new FormData()
  Form.append('file', image)

  //返回的数据
  let response = await fetch(`${BASE_URL}/search/file`, { method: 'POST', body: Form })
  console.log(response)

  // const colorResponse = await request.post(url, form)

  // let response
  // if (type === 'color') {
  //   response = await colorResponse.text()
  // } else {
  //   const bovwUrl = colorResponse.url.replace('/color/', '/bovw/')
  //   response = await request.get(bovwUrl).then(res => res.text())
  // }
  return json(parse(response))
}

import { FormData } from 'formdata-polyfill/esm.min.js'

import fs from 'fs'
//色彩检索
const type = 'color'
//特征检索
// const type = 'bovw'
const image = fs.createReadStream('../123.png')
await post({ type, image })
huankong233 commented 1 year ago

仓库地址

mnixry commented 1 year ago

500应该不是被拦了 应该是请求体什么写的不对 你再看看

huankong233 commented 1 year ago

嗯嗯,那我再去看看

huankong233 commented 1 year ago

这次应该真的是被拦截了( 信息有点多,就不全发出来了

        _currentUrl: 'https://ascii2d.obfs.dev/search/file',
        _timeout: null,
        [Symbol(kCapture)]: false
      },
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype] {
        accept: [ 'Accept', 'application/json, text/plain, */*' ],
        'content-type': [
          'Content-Type',
          'application/x-www-form-urlencoded;charset=utf-8'
        ],
        'user-agent': [ 'User-Agent', 'axios/1.1.3' ],
        'content-length': [ 'Content-Length', '24' ],
        'accept-encoding': [ 'Accept-Encoding', 'gzip, deflate, br' ],
        host: [ 'Host', 'ascii2d.obfs.dev' ]
      }
    },
    data: '<!DOCTYPE html>\n' +
      '<html>\n' +
      '<head>\n' +
      '<title>error (500)</title>\n' +
      '<script src="/cdn-cgi/apps/head/w0I4DjwRjFMi1e1fsDNCyVDhib8.js"></script></head>\n' +
      '<body>\n' +
      '<h1>error (500)</h1>\n' +
      `<script>(function(){var js = "window['__CF$cv$params']={r:'76e23d41386b9bf8',m:'tDEDQJVKEDGHumZSfYq8sxUwpITW8nraT4CCYXHHMDc-1669126138-0-ATwnB5j7PIRRrMj32A2jqGsii1/yppt6tRs+tWJ1PU0qARLEV+lKs2yM8SkWOnPJ7wTanZ4cxm1bgQE9p+B4aJlaVVjJPqWo0yn3fsVyPQti3EUVMJO9V/NZKps4tPcXww==',s:[0x44584262fd,0x6c3cfab7d5],u:'/cdn-cgi/challenge-platform/h/b'};var now=Date.now()/1000,offset=14400,ts=''+(Math.floor(now)-Math.floor(now%offset)),_cpo=document.createElement('script');_cpo.nonce='',_cpo.src='/cdn-cgi/challenge-platform/h/b/scripts/alpha/invisible.js?ts='+ts,document.getElementsByTagName('head')[0].appendChild(_cpo);";var _0xh = document.createElement('iframe');_0xh.height = 1;_0xh.width = 1;_0xh.style.position = 'absolute';_0xh.style.top = 0;_0xh.style.left = 0;_0xh.style.border = 'none';_0xh.style.visibility = 'hidden';document.body.appendChild(_0xh);function handler() {var _0xi = _0xh.contentDocument || _0xh.contentWindow.document;if (_0xi) {var _0xj = _0xi.createElement('script');_0xj.nonce = '';_0xj.innerHTML = js;_0xi.getElementsByTagName('head')[0].appendChild(_0xj);}}if (document.readyState !== 'loading') {handler();} else if (window.addEventListener) {document.addEventListener('DOMContentLoaded', handler);} else {var prev = document.onreadystatechange || function () {};document.onreadystatechange = function (e) {prev(e);if (document.readyState !== 'loading') {document.onreadystatechange = prev;handler();}};}})();</script><script defer src="https://static.cloudflareinsights.com/beacon.min.js/vaafb692b2aea4879b33c060e79fe94621666317369993" integrity="sha512-0ahDYl866UMhKuYcW078ScMalXqtFJggm7TmlUtp0UlD4eQk0Ixfnm5ykXKvGJNFjLMoortdseTfsRT8oCfgGA==" data-cf-beacon='{"rayId":"76e23d41386b9bf8","version":"2022.11.0","r":1,"token":"ce5a42621ac3434dacc5e4cab0b2c2d2","si":100}' crossorigin="anonymous"></script>\n` +
      '</body>\n' +
      '</html>\n'
  }
}
huankong233 commented 1 year ago
async function post(req) {
  const { type, image } = req

  const Form = new URLSearchParams()
  Form.append('file', image)

  //返回的数据
  let response = await axios({
    method: 'post',
    url: `${BASE_URL}/search/file`,
    timeout: 60000,
    // headers: {
    //   ...Form.getHeaders()
    // },
    data: Form
  })
  console.log(response)
}
huankong233 commented 1 year ago

或许我应该考虑传入url的方式来请求

huankong233 commented 1 year ago

IqDB如果不开启去除颜色会报错 image

import * as cheerio from 'cheerio'
import _ from 'lodash'

export const BASE_URL = 'https://iqdb.org/'

export function parse(body) {
  const $ = cheerio.load(body)
  return _.map($('table'), result => {
    const content = $(result).text(),
      [link] = $('td.image > a', result),
      [image] = $('td.image img', result)
    if (!link) return
    const [, similarity] = content.match(/(\d+%)\s*similarity/),
      //报错点
      [, resolution, level] = content.match(/(\d+×\d+)\s*\[(\w+)\]/)
    return {
      url: new URL(link.attribs.href, BASE_URL).toString(),
      image: new URL(image.attribs.src, BASE_URL).toString(),
      similarity: parseFloat(similarity),
      resolution,
      level: level.toLowerCase()
    }
  })
    .filter(v => v !== undefined)
    .sort((a, b) => a.similarity - b.similarity)
    .reverse()
}

import fetch from 'node-fetch'
import { FormData } from 'formdata-node'
import { fileFromPath } from 'formdata-node/file-from-path'

export async function post(req) {
  const { services, discolor, imagePath } = req
  const form = new FormData()
  form.append('file', await fileFromPath(imagePath))
  if (services) services.forEach((s, index) => form.append(`service.${index}`, s.toString()))
  if (discolor) form.append('forcegray', 'on')
  const response = await fetch(BASE_URL, { method: 'POST', body: form }).then(res => res.text())
  console.log(response)
  return parse(response)
}

const response = await post({
  // discolor: true,
  // 为false时会报错
  discolor: true,
  imagePath: '../123.png',
  services: [
    'danbooru',
    'konachan',
    'yandere',
    'gelbooru',
    'sankaku_channel',
    'e_shuushuu',
    'zerochan',
    'anime_pictures'
  ]
})
console.dir(response, { depth: null })