如何使用axios在其他网站填写并提交表单?

How do I fill in and submit a form on another website with axios?

我正在用 Node.js 制作一个鞋子抽奖机器人,最初使用无头 Puppeteer 来自动完成抽奖表格的填写和提交过程。有人告诉我 Puppeteer 非常 CPU-intensive 并且比 Node.js 中的请求模块慢,例如 fetch、Axios 等

这两天一直在弄Axios,但是我真的不知道怎么填写和提交表单。我如何填写和提交我在 Axios 中描述的表格?此外,Axios 是否会成为最佳选择(就速度和 CPU 使用而言)还是有更好的选择?

Here is an example for a form I would like to fill in.

这是我填写表格的木偶代码:

const { sizeSelectorsTitolo } = require('./selectors/sizes');
const  accounts  = require('./profiles/savedaccounts');

const { proxyList1 } = require('./profiles/proxylists');



async function titoloMain(url, size, shippingprofile , ppaccountnumber, proxygroup, instaaccountnumber){
    
    let splitProxy = proxygroup.split(':');
    let proxyUserLocal = splitProxy[2];
    let proxyPassLocal = splitProxy[3];
    let proxyPortLocal = splitProxy[1];
    let proxyMainLocal = splitProxy[0];
    let countrySelector = '';
    //Getting size selector ready
    let OurSizeSelector = 'sizeSelectorsTitolo.'
    OurSizeSelector = OurSizeSelector.concat(size);
    delete OurSizeSelector.property;
    OurSizeSelector = eval(OurSizeSelector);
        //Getting country selector
    switch (shippingprofile.country){
        case shippingprofile.country = "UK":
             countrySelector = 'United Kingdom'
                break;
        case shippingprofile.country = "USA":
             countrySelector = 'United States of America'
                break;
        case shippingprofile.country = "France":
             countrySelector = 'France'
                break;
        case shippingprofile.country = "Spain":
            countrySelector = 'Spain'
                break;
        case shippingprofile.country = "Germany":
            countrySelector = 'Germany'
                break;
        case shippingprofile.country = "Canada":
            countrySelector = 'Canada'
                break;                  
        };
        //getting gender selectors
        let localGender = '';
        if (shippingprofile.gender == 'Male'){
            localGender = 'Male'
        } else {
             localGender = 'Female'
        };
    const browser = await puppetteer.launch( {
        headless: true,
         args: ['--disable-infobars',
         `--window-size=${1000.},${1000.}`,
         '--disable-features=IsolateOrigins,site-per-process',
         //ip and port
        `${proxyMainLocal}:${proxyPortLocal}`
    ],
        ignoreDefaultArgs: ['--enable-automation']
    });
    const page = await browser.newPage();
        //proxy settings
        await page.authenticate({
            username: proxyUserLocal,
            password: proxyPassLocal
        });
        await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36');      
        //now at the raffle page
        console.log('on the raffle page')
        await page.goto(url, {waitUntil:"networkidle2"});
        console.log('filing out form...')
        console.log('paypal')
        await page.waitForSelector('#mce-EMAIL')
        await page.type('#mce-EMAIL',accounts.paypalAccounts[ppaccountnumber].main,{delay:20})
        console.log('name')
        await page.type('#mce-FNAME',shippingprofile.firstname, {delay:20});
        await page.type('#mce-LNAME',shippingprofile.surname, {delay:20});
        console.log('shipping address')
        await page.type('#mce-MMERGE5',shippingprofile.houseNum.concat(` ${shippingprofile.street}`), {delay:20});
        await page.type('#mce-MMERGE8', shippingprofile.postcode , {delay:20});
        await page.type('#mce-MMERGE12', shippingprofile.city , {delay:20});
        await page.select('#mce-MMERGE3',countrySelector);
        console.log('phone number')
        await page.type('#mce-PHONE', shippingprofile.phoneNumber, {delay:20});
        console.log('additional info')
        await page.select('#mce-MMERGE9',localGender);
        await page.select('#mce-MMERGE10','en');
        console.log('instagram name')
        await page.type('#mce-MMERGE7',accounts.instagram[instaaccountnumber].accountname,{delay:20});
        console.log('selecting size')
        await page.select('#mce-MMERGE6',OurSizeSelector)
        console.log('clicking terms')
        await page.waitForSelector('#mce-group\[199\]-199-0');
        await page.waitFor(500);
        await page.click('#mce-group\[199\]-199-0');
        console.log('submitting')
        await page.click('#mc-embedded-subscribe');
        await page.waitFor(1000);
        console.log(`done, check ${accounts.paypalAccounts[ppaccountnumber].main}`);
        await browser.close()
};

这是 axios 的代码,我不知道如何将输入字段定位到 post 数据,所以我现在只记录 headers:

const axios = require('axios')

async function test() {
    axios.get('https://en.titoloshop.com/titolo/air-jordan-1-retro-high-og-bio-hack/#raffle')
    .then(res => {
        console.log(res.headers)
    })
    .catch(err => {
        if (err.response) {
            console.log('there was an error');
            console.log(err.response.data);
            console.log(err.response.status);
            console.log(err.response.headers);
        }
    });
}

TLDR: 使用适当的表单数据发出 POST 请求,并对表单的 action 元素进行适当的编码。可以通过查看表单的 inputselect 元素的 name 属性找到要发送的键。向下滚动代码。


首先要知道Puppeteer and request modules such as axios and node-fetch的区别。 Puppeteer 用于控制浏览器并像用户使用网站一样使用网站,而 axios 用于简单地发出 HTTP 请求。因此,您无法执行单击按钮或填写表格等操作。

然而,提交表单通常只是简单地向另一个 URL 发出带有表单数据的 HTTP 请求。例如,这是抽奖表格的 HTML(简体):

<form
  action="https://titolo.us6.list-manage.com/subscribe/post?u=652f80ec0adf2d7ac9588d0a1&id=8093f364b8"
  method="post"
>
  <div>
    <label for="mce-EMAIL">Email Address* (PayPal)</label>
    <input type="email" name="EMAIL" id="mce-EMAIL">
  </div>
  <!-- first name, last name, address, postcode, city -->
  <div>
    <label for="mce-MMERGE3">Country*</label>
    <select name="MMERGE3" id="mce-MMERGE3">
      <option value=""></option>
      <option value="Switzerland">Switzerland</option>
      <!-- more countries... -->
    </select>
  </div>
  <!-- the rest of the inputs... -->
  <div>
    <strong>Terms & Conditions </strong>
      <ul>
        <li>
          <input type="checkbox" name="group[243][1]" id="mce-group[243]-243-0">
          <label for="mce-group[243]-243-0">I agree</label>
        </li>
      </ul>
  </div>
  <!-- more stuff... -->
  <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
    <div style="position: absolute; left: -5000px">
      <input
        type="text"
        name="b_652f80ec0adf2d7ac9588d0a1_8093f364b8"
        tabindex="-1"
        value=""
      >
    </div>
    <div>
      <input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe">
    </div>
</form>

我们来分解一下。

  • <form action="..." method="post'>:表单将POST其在post正文中的数据传送到URL中指定的action属性中。这将根据 enctype 属性进行编码。在这种情况下,它将是一个 application/x-www-form-urlencoded 字符串(例如 key1=value1&key2=value2),这是默认值。
  • <input type="email" name="EMAIL" id="mce-EMAIL">:输入的邮件将是post正文中EMAIL键(由name属性表示)的值。
  • <select name="MMERGE3" id="mce-MMERGE3">:selected 选项将是 MMERGE3 键的值。
    • <option value="Switzerland"> 每个选项都有一个 value 属性,它指定要在 post 正文中发送的字符串。这不需要与文本内容相同。如果不存在,将发送元素的文本内容。
  • <input type="checkbox" value="1" name="group[243][1]" id="mce-group[243]-243-0"> 这是一个复选框输入。如果复选框是 selected,group[243][1]name 属性)将是 1value 属性,默认为 on)在表单正文中。如果不 selected,group[243][1] 将不存在于正文中。
  • <input type="text" name="b_652f80ec0adf2d7ac9588d0a1_8093f364b8" tabindex="-1" value=""> 根据评论,这是一个永远不应该填写的输入,只是为了防止机器人注册抽奖(讽刺是不是?)。
  • <input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe"> 这是提交按钮。除非有 name 属性(就像任何其他 input 一样),否则不会提交此值,因此在这种情况下,将有一个名为 subscribe 的键,其值为 [=43] =](来自 value 属性)。默认 value 属性因浏览器而异:
    <input type="submit">
    

有关提交表单如何工作的详细信息,请参阅 Sending form data on MDN


因此,要在不使用 Puppeteer 的情况下提交此表单,您需要使用适当的 URL-encoded 表单值作为表单数据来发出 POST 请求。您可以像这样在 axios 中执行此操作 (documentation):

const url = 'https://titolo.us6.list-manage.com/subscribe/post?u=652f80ec0adf2d7ac9588d0a1&id=8093f364b8'
axios.post(url, new URLSearchParams({
  EMAIL: accounts.paypalAccounts[ppaccountnumber].main,
  FNAME: shippingprofile.firstname,
  LNAME: shippingprofile.surname,
  // Address
  MMERGE5: shippingprofile.houseNum.concat(` ${shippingprofile.street}`),
  // Postcode
  MMERGE8: shippingprofile.postcode,
  // City
  MMERGE12: shippingprofile.city,
  // Country
  MMERGE3: countrySelector,
  PHONE: shippingprofile.phoneNumber,
  // Gender
  MMERGE9: localGender,
  // Language
  MMERGE10: 'en',
  // Size
  MMERGE6: OurSizeSelector,
  // Instagram account name
  MMERGE7: accounts.instagram[instaaccountnumber].accountname,
  // T&Cs
  'group[243][1]': '1',
  // That invisible input
  b_652f80ec0adf2d7ac9588d0a1_8093f364b8: '',
  // Subscribe button
  subscribe: 'Subscribe'
}))

此代码应提交表单,就像用户在其网站上填写并提交表单一样。很可能并非所有这些都是实际需要的(例如订阅按钮和不可见输入),但我将它们包括在这里以防万一。

注意:该网站要求您 select 您希望从公司那里听到的方式:时事通讯 (name="gdpr[695]") and/or 抽奖信息 (name="gdpr[699]" ).这两个复选框都有 value="Y",所以如果您想将它们作为表单的一部分提交,只需将 'gdpr[695]': 'Y' and/or 'gdpr[699]': 'Y' 添加到 URLSearchParams 构造函数。